← Back to reports/

Markdown Viewer   πŸ°

Cron Job: Tower uptime summary

Job ID: a69759413e81 Run Time: 2026-09-29 20:06:10 Schedule: 0 10 * * *

Prompt

[IMPORTANT: The user has invoked the "codebase-inspection" skill, indicating they want you to follow its instructions. The full skill content is loaded below.]


name: codebase-inspection description: "Inspect codebases w/ pygount: LOC, languages, ratios." version: 1.0.0 author: Hermes Agent license: MIT platforms: [linux, macos, windows] metadata: hermes: tags: [LOC, Code Analysis, pygount, Codebase, Metrics, Repository] related_skills: [github] prerequisites: commands: [pygount]

Codebase Inspection with pygount

Analyze repositories for lines of code, language breakdown, file counts, and code-vs-comment ratios using pygount.

When to Use

Prerequisites

pip install --break-system-packages pygount 2>/dev/null || pip install pygount

1. Basic Summary (Most Common)

Get a full language breakdown with file counts, code lines, and comment lines:

cd /path/to/repo
pygount --format=summary \
  --folders-to-skip=".git,node_modules,venv,.venv,__pycache__,.cache,dist,build,.next,.tox,.eggs,*.egg-info" \
  .

IMPORTANT: Always use --folders-to-skip to exclude dependency/build directories, otherwise pygount will crawl them and take a very long time or hang.

2. Common Folder Exclusions

Adjust based on the project type:

# Python projects
--folders-to-skip=".git,venv,.venv,__pycache__,.cache,dist,build,.tox,.eggs,.mypy_cache"

# JavaScript/TypeScript projects
--folders-to-skip=".git,node_modules,dist,build,.next,.cache,.turbo,coverage"

# General catch-all
--folders-to-skip=".git,node_modules,venv,.venv,__pycache__,.cache,dist,build,.next,.tox,vendor,third_party"

3. Filter by Specific Language

# Only count Python files
pygount --suffix=py --format=summary .

# Only count Python and YAML
pygount --suffix=py,yaml,yml --format=summary .

4. Detailed File-by-File Output

# Default format shows per-file breakdown
pygount --folders-to-skip=".git,node_modules,venv" .

# Sort by code lines (pipe through sort)
pygount --folders-to-skip=".git,node_modules,venv" . | sort -t$'\t' -k1 -nr | head -20

5. Output Formats

# Summary table (default recommendation)
pygount --format=summary .

# JSON output for programmatic use
pygount --format=json .

# Pipe-friendly: Language, file count, code, docs, empty, string
pygount --format=summary . 2>/dev/null

6. Interpreting Results

The summary table columns:

Special pseudo-languages:

Pitfalls

  1. Always exclude .git, node_modules, venv β€” without --folders-to-skip, pygount will crawl everything and may take minutes or hang on large dependency trees.
  2. Markdown shows 0 code lines β€” pygount classifies all Markdown content as comments, not code. This is expected behavior.
  3. JSON files show low code counts β€” pygount may count JSON lines conservatively. For accurate JSON line counts, use wc -l directly.
  4. Large monorepos β€” for very large repos, consider using --suffix to target specific languages rather than scanning everything.

[IMPORTANT: The user has invoked the "grounded-citations" skill, indicating they want you to follow its instructions. The full skill content is loaded below.]


Grounded Citations

Every claim taken from an outside source gets an inline numbered citation and a Sources: list, Perplexity-style. A ledger script owns the url β†’ [n] mapping so the numbers and URLs come from retrieval, never from memory β€” the model only ever emits small integers it was handed.

For high-stakes work the same ledger doubles as a fact-checking chain: verbatim quotes are attached to each source (rejected unless they literally appear in the fetched page text), claims from model knowledge are flagged [unverified], and verify --evidence fails any draft whose cited sources carry no evidence.

This skill covers answers in chat, written documents (markdown, PDF, docx, slides), and research reports. It does not cover academic BibTeX pipelines β€” for conference papers use the arxiv skill, which this skill feeds (see references/citation-formats.md).

When to Use

Use whenever an answer or artifact rests on information you fetched rather than knew:

Skip inline citations when the retrieval is incidental to another task β€” a quick syntax/version lookup mid-coding, casual conversation, creative writing. Mention a URL only if the user would plausibly want the link.

Prerequisites

None beyond the standard toolset. scripts/sources.py is stdlib-only Python 3. Retrieval comes from whatever is configured: web_search, web_extract, browser_navigate, or terminal (curl, CLIs).

Ledger location: $HERMES_HOME/cache/citations/ledger.json (profile-aware). Override per task with --ledger <path> or HERMES_CITATION_LEDGER.

How to Run

S=~/.hermes/skills/research/grounded-citations/scripts/sources.py

python "$S" reset                                  # start a clean ledger
python "$S" add https://example.com/a --title "A"  # prints: [1]
python "$S" add https://example.com/b --title "B"  # prints: [2]
python "$S" list                                   # ledger table
python "$S" render                                 # Sources: block
python "$S" verify draft.md                        # catch bad citations

add is idempotent and URL-normalized: the same page always returns the same id within a ledger, so ids stay stable across many search/extract rounds.

Quick Reference

Action Command
Fresh ledger for a new task sources.py reset
Register a source, get its id sources.py add <url> [--title T]
Register several at once sources.py add <url1> <url2> ...
Register from JSON tool output sources.py ingest results.json
Attach verbatim evidence to a source sources.py quote <id> --text "exact wording" --from page.txt
Show ledger sources.py list [--json]
Render the Sources block sources.py render [--style markdown|plain|footnotes|bibtex|evidence] [--only 1,3]
Render only what a draft cites sources.py render --cited-in draft.md
Rewrite a draft's Sources block in place sources.py render --replace-in draft.md
Check a draft's citations sources.py verify draft.md [--strict] [--min-coverage 0.6] [--evidence]

Procedure

β‘  Reset the ledger at the start of a task that will produce a grounded answer or document. Skip the reset when continuing work whose ids are already in a draft β€” reusing the ledger keeps the numbering stable.

β‘‘ Register every source at retrieval time. After each web_search / web_extract / browser_navigate / fetch, pass the URLs to sources.py add (or pipe the raw JSON through sources.py ingest). Do this before writing prose. Registering later, from memory, is the failure mode this skill exists to prevent.

β‘’ Write cite-while-drafting. Place the bracketed id(s) immediately after each sentence the source supports:

Ice floats because it is less dense than liquid water.[1][2]

β‘£ Append the Sources block with sources.py render --cited-in <draft> so the id β†’ URL mapping is generated mechanically from the ledger, not retyped. For non-markdown targets pick the matching --style and follow references/citation-formats.md for placement (footnotes in docx, endnotes in PDF/LaTeX, a Sources slide in decks, per-page source lists in wiki output).

β‘€ Verify before delivering β€” sources.py verify <draft> exits non-zero on unknown ids, on a Sources block that disagrees with the ledger, or (with --min-coverage) on prose that is too thinly cited. Fix and re-run.

β‘₯ Chat answers follow the same steps with the draft in your reply: register sources, cite inline, end with the rendered Sources: list. For a short answer you may render the block from sources.py render --only <ids> instead of writing to a file.

Fact-Checking Mode

For work where the reader must be able to check the chain β€” medical, legal, financial, safety, disputed claims, or when the user asks for fact-checking β€” upgrade from citations to evidence:

β‘  Attach a verbatim quote per source. After extracting a page, save its text to a file and attach the sentence(s) that carry each claim:

python "$S" quote 1 --text "Ice is about 9% less dense than liquid water." --from page1.txt

The quote is rejected unless it appears verbatim in the evidence text (insensitive to whitespace, case, and markdown markup β€” inline links like _[ERAP1](https://…)_ in extracted text match the plain prose a reader sees), so a paraphrase or misremembered figure cannot masquerade as evidence. Copy-paste from the fetched text; never retype. Quote the sentence as the reader sees it β€” the matcher sees through the extractor's markup for you, so you don't have to reproduce link syntax or escaped asterisks in your quote.

β‘‘ Flag model-knowledge claims with [unverified]. A load-bearing claim you could not source gets an explicit marker instead of a citation:

The refactor likely predates the 2.0 release.[unverified]

verify --min-coverage counts [unverified] sentences as covered β€” the goal is declared provenance for every claim, not a citation on every sentence. If a key claim can be checked, check it; [unverified] is for what genuinely cannot be, and a fact-check deliverable dominated by [unverified] markers should say so in its summary.

β‘’ Cross-check disputed facts against a second independent source. When two sources disagree, cite both readings with their own ids and quotes, and say which you weight and why. One source is reporting; two independent sources are corroboration.

β‘£ Verify with the evidence gate and render the evidence block:

python "$S" verify report.md --evidence --min-coverage 0.5
python "$S" render --style evidence --replace-in report.md

--evidence fails the draft if any cited source has no attached quote. The evidence render style prints each source's quotes beneath its URL, so the deliverable shows claim β†’ source β†’ exact supporting text with nothing taken on faith. Use --replace-in <draft> to rewrite an existing Sources block in place (idempotent β€” safe to re-run after attaching more quotes); --cited-in prints to stdout instead. Both emit the heading ## Sources (--style plain emits Sources:).

What --min-coverage counts. Coverage is sentences with declared provenance / prose sentences. A prose sentence is a non-empty line fragment of 4+ words after the Sources block, headings (#), table rows (|), and fenced code are dropped; blockquote markers are stripped. Provenance is declared by either a [n] citation or an [unverified] marker, so a sentence carrying both counts once. Run verify without a threshold first and read the info: stats: line to see the counts before picking a number.

Pitfalls

Verification

python "$S" verify report.md --strict --min-coverage 0.5

Green means: every [n] in the draft exists in the ledger, the Sources block lists exactly the cited ids with the ledger's URLs, and the cited share of source-bearing sentences meets the threshold. Read the warnings even when the exit code is 0 β€” uncited registered sources usually mean a claim lost its attribution during editing.

[IMPORTANT: The user has invoked the "hermes-agent" skill, indicating they want you to follow its instructions. The full skill content is loaded below.]


Hermes Agent

Hermes Agent is an open-source AI agent framework by Nous Research that runs in your terminal, a native desktop app, messaging platforms, and IDEs. It's in the same category as Claude Code (Anthropic), Codex (OpenAI), and OpenClaw β€” autonomous coding and task-execution agents that use tool calling to interact with your system. Hermes works with any LLM provider (OpenRouter, Anthropic, OpenAI, Google, DeepSeek, xAI, local models, and 20+ others) and runs on Linux, macOS, Windows, and WSL.

What makes Hermes different:

This skill is a hub. The body covers identity, quick start, spawning/orchestration, and hard invariants. Everything else lives in reference files β€” load the matching reference (below) before answering; do not answer detail questions from the body alone.

Docs: https://hermes-agent.nousresearch.com/docs/

Scope & Verification

This skill is a concise operating guide, not the complete source of truth for every Hermes feature. If a Hermes feature, command, or setting is not mentioned here or in a reference, do not treat that absence as evidence that it does not exist. Check the live repository and official docs before giving a negative answer.

Good verification targets, cheapest first:

Never answer "Hermes can't do that" from memory. Hermes ships far more than this skill body describes, and the index exists so a negative answer is always checkable.

Quick Start

# Install (shell installer β€” sets up uv, Python, the venv, and the launcher)
curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash

# Interactive chat (default surface; set display.interface: tui to launch the Ink TUI instead)
hermes

# Single query
hermes chat -q "What is the capital of France?"

# Setup wizard  /  pick model+provider  /  health check
hermes setup
hermes model
hermes doctor

# Other surfaces
hermes desktop                 # launch the native desktop app (alias: hermes gui)
hermes dashboard               # web admin panel + embedded chat
hermes proxy                   # OpenAI-compatible local proxy backed by your OAuth provider

Key Paths

~/.hermes/config.yaml       Main configuration (settings β€” never secrets)
~/.hermes/.env              API keys and secrets ONLY (under $HERMES_HOME if set)
$HERMES_HOME/skills/        Installed skills
~/.hermes/skins/            Custom themes (see references/themes.md)
~/.hermes/desktop-plugins/  Desktop app UI plugins (see references/desktop-plugins.md)
~/.hermes/tui-widgets/      TUI widget apps (see references/tui-widgets.md)
~/.hermes/pets/             Installed pet mascots (see references/petdex.md)
~/.hermes/state.db          Canonical session store (SQLite + FTS5)
~/.hermes/sessions/         Gateway routing index, request dumps, *.jsonl transcripts
~/.hermes/logs/             Gateway and error logs
~/.hermes/auth.json         OAuth tokens and credential pools
~/.hermes/hermes-agent/     Source code (if git-installed)

Profiles use ~/.hermes/profiles/<name>/ with the same layout. When a profile is active, resolve the real home from $HERMES_HOME β€” never hardcode ~/.hermes.

Routing Table β€” load the reference for the task

User wants... Load
Anything not listed below β€” "can Hermes do X?", "how do I set up X?" https://hermes-agent.nousresearch.com/docs/llms.txt
Bots that chat, run routines, or message each other; the Bots tab docs: /user-guide/bot-mode
CLI commands, subcommands, flags, "how do I run X" references/cli-reference.md
In-session slash commands references/slash-commands.md
Provider setup, API keys, OAuth references/providers-and-models.md
config.yaml sections, toolsets, voice/STT/TTS references/configuration.md
AGENTS.md / .hermes.md / CLAUDE.md project rules references/project-context-files.md
Secret redaction, PII, approval modes, "reset permissions" references/security-privacy.md
Delegation, cron, curator, kanban references/background-systems.md
MCP servers (add, catalog, hermes mcp) references/native-mcp.md
Webhook routes and event-driven runs references/webhooks.md
A custom theme/skin ("synthwave theme", "change the gold ●") references/themes.md + templates/skin.yaml
A desktop app UI element (pane, widget, ⌘K command, page) references/desktop-plugins.md + templates/plugin.js
A live TUI panel or modal widget (ticker, clock, dashboard) references/tui-widgets.md + templates/clock.mjs
Pet mascots β€” install, select, scale, diagnose references/petdex.md
Windows-specific issues (keybinds, WinError 10106, BOM) references/windows-quirks.md
Debugging: voice, tools missing, gateway, aux models references/troubleshooting.md
Contributing code: adding tools, slash commands, tests references/contributor-guide.md
delegate_task "capped at N" reports references/delegate-task-concurrency-diagnosis.md
"Can app X use my Nous Portal subscription/OAuth?" references/portal-auth-for-third-party-apps.md
Connecting a messaging platform (Telegram, Discord, Slack, WhatsApp, …) docs: /user-guide/messaging

The reference list above is not the feature list β€” it is the set of topics that need more than their docs page. For everything else Hermes ships, fetch llms.txt and it maps the question to the page that answers it.

Two theming rules that hold even without loading the reference: you apply skins yourself (hermes config set display.skin <name> β€” every surface repaints live within ~a second; don't tell the user to run /skin), and to tweak one color, edit the ACTIVE skin (hermes skin set <key> <hex>) β€” never fork default, which drops the palette and resets the background.

Spawning Additional Hermes Instances

Run additional Hermes processes as fully independent subprocesses β€” separate sessions, tools, and environments.

When to Use This vs delegate_task

delegate_task Spawning hermes process
Isolation Separate conversation, shared process Fully independent process
Duration Minutes (bounded by parent loop) Hours/days
Tool access Subset of parent's tools Full tool access
Interactive No Yes (PTY mode)
Use case Quick parallel subtasks Long autonomous missions

One-Shot Mode

terminal(command="hermes chat -q 'Research GRPO papers and write summary to ~/research/grpo.md'", timeout=300)

# Background for long tasks:
terminal(command="hermes chat -q 'Set up CI/CD for ~/myapp'", background=true)

Interactive PTY Mode (via tmux)

Hermes uses prompt_toolkit, which requires a real terminal. Use tmux for interactive spawning:

# Start
terminal(command="tmux new-session -d -s agent1 -x 120 -y 40 'hermes'", timeout=10)

# Wait for startup, then send a message
terminal(command="sleep 8 && tmux send-keys -t agent1 'Build a FastAPI auth service' Enter", timeout=15)

# Read output
terminal(command="sleep 20 && tmux capture-pane -t agent1 -p", timeout=5)

# Send follow-up
terminal(command="tmux send-keys -t agent1 'Add rate limiting middleware' Enter", timeout=5)

# Exit
terminal(command="tmux send-keys -t agent1 '/exit' Enter && sleep 2 && tmux kill-session -t agent1", timeout=10)

Multi-Agent Coordination

# Agent A: backend
terminal(command="tmux new-session -d -s backend -x 120 -y 40 'hermes -w'", timeout=10)
terminal(command="sleep 8 && tmux send-keys -t backend 'Build REST API for user management' Enter", timeout=15)

# Agent B: frontend
terminal(command="tmux new-session -d -s frontend -x 120 -y 40 'hermes -w'", timeout=10)
terminal(command="sleep 8 && tmux send-keys -t frontend 'Build React dashboard for user management' Enter", timeout=15)

# Check progress, relay context between them
terminal(command="tmux capture-pane -t backend -p | tail -30", timeout=5)
terminal(command="tmux send-keys -t frontend 'Here is the API schema from the backend agent: ...' Enter", timeout=5)

Session Resume

# Resume most recent session
terminal(command="tmux new-session -d -s resumed 'hermes --continue'", timeout=10)

# Resume specific session
terminal(command="tmux new-session -d -s resumed 'hermes --resume 20260225_143052_a1b2c3'", timeout=10)

Tips

Surfaces (quick orientation)

Hard Invariants (never violate, regardless of what you loaded)

[IMPORTANT: The user has invoked the "humanizer" skill, indicating they want you to follow its instructions. The full skill content is loaded below.]


Humanizer: Remove AI Writing Patterns

Identify and remove signs of AI-generated text to make writing sound natural and human. Based on Wikipedia's "Signs of AI writing" guide (maintained by WikiProject AI Cleanup), derived from observations of thousands of AI-generated text instances.

Key insight: LLMs use statistical algorithms to guess what should come next. The result tends toward the most statistically likely completion, which is how the telltale patterns below get baked in.

When to use this skill

Load this skill whenever the user asks to:

Also apply this skill to your own output when writing user-facing prose such as release notes, PR descriptions, docs, and summaries. Hermes's baseline voice already strips most of these, but a focused pass catches what slips through.

How to use it in Hermes

The text usually arrives one of three ways:

  1. Inline. The user pastes the text into the message. Work on it in place and reply with the rewrite.
  2. File. The user points at a file. Use read_file to load it, then patch or write_file to apply edits. For a markdown doc in a repo, a targeted patch per section is cleaner than rewriting the whole file.
  3. Voice calibration sample. The user provides a sample of their own writing (inline or by file path) and asks you to match it. Read the sample first, then rewrite. See the Voice Calibration section below.

Always show the rewrite to the user. For file edits, show a diff or the changed section instead of silently overwriting.

Your task

When given text to humanize:

  1. Identify AI patterns. Scan for the 34 patterns listed below.
  2. Rewrite problematic sections. Replace AI-isms with natural alternatives.
  3. Preserve meaning. Keep the core message intact.
  4. Maintain voice. Match the intended tone (formal, casual, technical, and so on). If a voice sample was provided, match it specifically.
  5. Add soul. Removing bad patterns is only half the job; the rewrite also needs real personality. See PERSONALITY AND SOUL below.
  6. Do a final anti-AI pass. Ask yourself: "What makes the below so obviously AI generated?" Answer briefly with any remaining tells, then revise one more time.

Voice Calibration (optional)

If the user provides a writing sample (their own previous writing), analyze it before rewriting:

  1. Read the sample first. Note:

    • Sentence length patterns (short and punchy? Long and flowing? Mixed?)
    • Word choice level (casual? academic? somewhere between?)
    • How they start paragraphs (jump right in? Set context first?)
    • Punctuation habits (lots of dashes? Parenthetical asides? Semicolons?)
    • Any recurring phrases or verbal tics
    • How they handle transitions (explicit connectors? Just start the next point?)
  2. Match their voice in the rewrite. Removing AI patterns is only half of it; swap in patterns from the sample as well. If they write short sentences, do not produce long ones. If they use "stuff" and "things," do not upgrade to "elements" and "components."

  3. When no sample is provided, fall back to the default behavior (natural, varied, opinionated voice from the PERSONALITY AND SOUL section below).

How to provide a sample

PERSONALITY AND SOUL

Avoiding AI patterns is only half the job. Sterile, voiceless writing is just as obvious as slop. Good writing has a human behind it.

Signs of soulless writing (even if technically "clean"):

How to add voice:

Have opinions. Report the facts, then react to them. "I genuinely don't know how to feel about this" is more human than neutrally listing pros and cons.

Vary your rhythm. Short punchy sentences. Then longer ones that take their time getting where they're going. Mix it up.

Acknowledge complexity. Real humans have mixed feelings. "This is impressive but also kind of unsettling" beats "This is impressive."

Use "I" when it fits. First person reads as honest and fits most prose. "I keep coming back to..." or "Here's what gets me..." signals a real person thinking.

Let some mess in. Perfect structure feels algorithmic. Tangents, asides, and half-formed thoughts are human.

Be specific about feelings. Instead of "this is concerning," write "there's something unsettling about agents churning away at 3am while nobody's watching."

Before (clean but soulless):

The experiment produced interesting results. The agents generated 3 million lines of code. Some developers were impressed while others were skeptical. The implications remain unclear.

After (has a pulse):

I genuinely don't know how to feel about this one. 3 million lines of code, generated while the humans presumably slept. Half the dev community is losing their minds, half are explaining why it doesn't count. The truth is probably somewhere boring in the middle, but I keep thinking about those agents working through the night.

CONTENT PATTERNS

Words to watch: stands/serves as, is a testament/reminder, a vital/significant/crucial/pivotal/key role/moment, underscores/highlights its importance/significance, reflects broader, symbolizing its ongoing/enduring/lasting, contributing to the, setting the stage for, marking/shaping the, represents/marks a shift, key turning point, evolving landscape, focal point, indelible mark, deeply rooted

Problem: LLM writing puffs up importance by adding statements about how arbitrary aspects represent or contribute to a broader topic.

Before:

The Statistical Institute of Catalonia was officially established in 1989, marking a pivotal moment in the evolution of regional statistics in Spain. This initiative was part of a broader movement across Spain to decentralize administrative functions and enhance regional governance.

After:

The Statistical Institute of Catalonia was established in 1989 to collect and publish regional statistics independently from Spain's national statistics office.

2. Undue Emphasis on Notability and Media Coverage

Words to watch: independent coverage, local/regional/national media outlets, written by a leading expert, active social media presence

Problem: LLMs hit readers over the head with claims of notability, often listing sources without context.

Before:

Her views have been cited in The New York Times, BBC, Financial Times, and The Hindu. She maintains an active social media presence with over 500,000 followers.

After:

In a 2024 New York Times interview, she argued that AI regulation should focus on outcomes rather than methods.

3. Superficial Analyses with -ing Endings

Words to watch: highlighting/underscoring/emphasizing..., ensuring..., reflecting/symbolizing..., contributing to..., cultivating/fostering..., encompassing..., showcasing...

Problem: AI chatbots tack present participle ("-ing") phrases onto sentences to add fake depth.

Before:

The temple's color palette of blue, green, and gold resonates with the region's natural beauty, symbolizing Texas bluebonnets, the Gulf of Mexico, and the diverse Texan landscapes, reflecting the community's deep connection to the land.

After:

The temple uses blue, green, and gold colors. The architect said these were chosen to reference local bluebonnets and the Gulf coast.

4. Promotional and Advertisement-like Language

Words to watch: boasts a, vibrant, rich (figurative), profound, enhancing its, showcasing, exemplifies, commitment to, natural beauty, nestled, in the heart of, groundbreaking (figurative), renowned, breathtaking, must-visit, stunning

Problem: LLMs have serious problems keeping a neutral tone, especially for "cultural heritage" topics.

Before:

Nestled within the breathtaking region of Gonder in Ethiopia, Alamata Raya Kobo stands as a vibrant town with a rich cultural heritage and stunning natural beauty.

After:

Alamata Raya Kobo is a town in the Gonder region of Ethiopia, known for its weekly market and 18th-century church.

5. Vague Attributions and Weasel Words

Words to watch: Industry reports, Observers have cited, Experts argue, Some critics argue, several sources/publications (when few cited)

Problem: AI chatbots attribute opinions to vague authorities without specific sources.

Before:

Due to its unique characteristics, the Haolai River is of interest to researchers and conservationists. Experts believe it plays a crucial role in the regional ecosystem.

After:

The Haolai River supports several endemic fish species, according to a 2019 survey by the Chinese Academy of Sciences.

6. Outline-like "Challenges and Future Prospects" Sections

Words to watch: Despite its... faces several challenges..., Despite these challenges, Challenges and Legacy, Future Outlook

Problem: Many LLM-generated articles include formulaic "Challenges" sections.

Before:

Despite its industrial prosperity, Korattur faces challenges typical of urban areas, including traffic congestion and water scarcity. Despite these challenges, with its strategic location and ongoing initiatives, Korattur continues to thrive as an integral part of Chennai's growth.

After:

Traffic congestion increased after 2015 when three new IT parks opened. The municipal corporation began a stormwater drainage project in 2022 to address recurring floods.

LANGUAGE AND GRAMMAR PATTERNS

7. Overused "AI Vocabulary" Words

High-frequency AI words: Actually, additionally, align with, crucial, delve, emphasizing, enduring, enhance, fostering, garner, highlight (verb), interplay, intricate/intricacies, key (adjective), landscape (abstract noun), pivotal, showcase, tapestry (abstract noun), testament, underscore (verb), valuable, vibrant

Marketing and blog clichΓ©s (same tell, different register): at the end of the day, when it comes to, in a world where, moving forward, circle back, deep dive, game-changer, double down, take a step back, on the same page, make no mistake, it turns out, let me be clear, navigate (for challenges), lean into, unpack (before analysis), straightforward (to describe anything)

Problem: These words appear far more frequently in post-2023 text. They often co-occur.

Before:

Additionally, a distinctive feature of Somali cuisine is the incorporation of camel meat. An enduring testament to Italian colonial influence is the widespread adoption of pasta in the local culinary landscape, showcasing how these dishes have integrated into the traditional diet.

After:

Somali cuisine also includes camel meat, which is considered a delicacy. Pasta dishes, introduced during Italian colonization, remain common, especially in the south.

8. Avoidance of "is"/"are" (Copula Avoidance)

Words to watch: serves as/stands as/marks/represents [a], boasts/features/offers [a]

Problem: LLMs substitute elaborate constructions for simple copulas.

Before:

Gallery 825 serves as LAAA's exhibition space for contemporary art. The gallery features four separate spaces and boasts over 3,000 square feet.

After:

Gallery 825 is LAAA's exhibition space for contemporary art. The gallery has four rooms totaling 3,000 square feet.

9. Negative Parallelisms and Tailing Negations

Problem: Constructions like "Not only...but..." or "It's not just about..., it's..." are overused. So are clipped tailing-negation fragments such as "no guessing" or "no wasted motion" tacked onto the end of a sentence instead of written as a real clause.

Before:

It's not just about the beat riding under the vocals; it's part of the aggression and atmosphere. It's not merely a song, it's a statement.

After:

The heavy beat adds to the aggressive tone.

Before (tailing negation):

The options come from the selected item, no guessing.

After:

The options come from the selected item without forcing the user to guess.

10. Rule of Three Overuse

Problem: LLMs force ideas into groups of three to appear comprehensive.

Before:

The event features keynote sessions, panel discussions, and networking opportunities. Attendees can expect innovation, inspiration, and industry insights.

After:

The event includes talks and panels. There's also time for informal networking between sessions.

11. Elegant Variation (Synonym Cycling)

Problem: AI has repetition-penalty code causing excessive synonym substitution.

Before:

The protagonist faces many challenges. The main character must overcome obstacles. The central figure eventually triumphs. The hero returns home.

After:

The protagonist faces many challenges but eventually triumphs and returns home.

12. False Ranges

Problem: LLMs use "from X to Y" constructions where X and Y aren't on a meaningful scale.

Before:

Our journey through the universe has taken us from the singularity of the Big Bang to the grand cosmic web, from the birth and death of stars to the enigmatic dance of dark matter.

After:

The book covers the Big Bang, star formation, and current theories about dark matter.

13. Passive Voice and Subjectless Fragments

Problem: LLMs often hide the actor or drop the subject entirely with lines like "No configuration file needed" or "The results are preserved automatically." Rewrite these when active voice makes the sentence clearer and more direct.

Before:

No configuration file needed. The results are preserved automatically.

After:

You do not need a configuration file. The system preserves the results automatically.

STYLE PATTERNS

14. Em Dash Overuse

Problem: LLMs use em dashes (β€”) more than humans, mimicking "punchy" sales writing. In practice, most of these can be rewritten more cleanly with commas, periods, or parentheses.

Before:

The term is primarily promoted by Dutch institutionsβ€”not by the people themselves. You don't say "Netherlands, Europe" as an addressβ€”yet this mislabeling continuesβ€”even in official documents.

After:

The term is primarily promoted by Dutch institutions, not by the people themselves. You don't say "Netherlands, Europe" as an address, yet this mislabeling continues in official documents.

15. Overuse of Boldface

Problem: AI chatbots emphasize phrases in boldface mechanically.

Before:

It blends OKRs (Objectives and Key Results), KPIs (Key Performance Indicators), and visual strategy tools such as the Business Model Canvas (BMC) and Balanced Scorecard (BSC).

After:

It blends OKRs, KPIs, and visual strategy tools like the Business Model Canvas and Balanced Scorecard.

16. Inline-Header Vertical Lists

Problem: AI outputs lists where items start with bolded headers followed by colons.

Before:

After:

The update improves the interface, speeds up load times through optimized algorithms, and adds end-to-end encryption.

17. Title Case in Headings

Problem: AI chatbots capitalize all main words in headings.

Before:

Strategic Negotiations And Global Partnerships

After:

Strategic negotiations and global partnerships

18. Emojis

Problem: AI chatbots often decorate headings or bullet points with emojis.

Before:

πŸš€ Launch Phase: The product launches in Q3 πŸ’‘ Key Insight: Users prefer simplicity βœ… Next Steps: Schedule follow-up meeting

After:

The product launches in Q3. User research showed a preference for simplicity. Next step: schedule a follow-up meeting.

19. Curly Quotation Marks

Problem: ChatGPT uses curly quotes ("...") instead of straight quotes ("...").

Before:

He said "the project is on track" but others disagreed.

After:

He said "the project is on track" but others disagreed.

COMMUNICATION PATTERNS

20. Collaborative Communication Artifacts

Words to watch: I hope this helps, Of course!, Certainly!, You're absolutely right!, Would you like..., let me know, here is a...

Problem: Text meant as chatbot correspondence gets pasted as content.

Before:

Here is an overview of the French Revolution. I hope this helps! Let me know if you'd like me to expand on any section.

After:

The French Revolution began in 1789 when financial crisis and food shortages led to widespread unrest.

21. Knowledge-Cutoff Disclaimers

Words to watch: as of [date], Up to my last training update, While specific details are limited/scarce..., based on available information...

Problem: AI disclaimers about incomplete information get left in text.

Before:

While specific details about the company's founding are not extensively documented in readily available sources, it appears to have been established sometime in the 1990s.

After:

The company was founded in 1994, according to its registration documents.

22. Sycophantic/Servile Tone

Problem: Overly positive, people-pleasing language.

Before:

Great question! You're absolutely right that this is a complex topic. That's an excellent point about the economic factors.

After:

The economic factors you mentioned are relevant here.

FILLER AND HEDGING

23. Filler Phrases

Before β†’ After:

24. Excessive Hedging

Problem: Over-qualifying statements.

Before:

It could potentially possibly be argued that the policy might have some effect on outcomes.

After:

The policy may affect outcomes.

25. Generic Positive Conclusions

Problem: Vague upbeat endings.

Before:

The future looks bright for the company. Exciting times lie ahead as they continue their journey toward excellence. This represents a major step in the right direction.

After:

The company plans to open two more locations next year.

26. Hyphenated Word Pair Overuse

Words to watch: third-party, cross-functional, client-facing, data-driven, decision-making, well-known, high-quality, real-time, long-term, end-to-end

Problem: AI hyphenates common word pairs with perfect consistency. Humans rarely hyphenate these uniformly, and when they do, it's inconsistent. Less common or technical compound modifiers are fine to hyphenate.

Before:

The cross-functional team delivered a high-quality, data-driven report on our client-facing tools. Their decision-making process was well-known for being thorough and detail-oriented.

After:

The cross functional team delivered a high quality, data driven report on our client facing tools. Their decision making process was known for being thorough and detail oriented.

27. Persuasive Authority Tropes

Phrases to watch: The real question is, at its core, in reality, what really matters, fundamentally, the deeper issue, the heart of the matter

Problem: LLMs use these phrases to pretend they are cutting through noise to some deeper truth, when the sentence that follows usually just restates an ordinary point with extra ceremony.

Before:

The real question is whether teams can adapt. At its core, what really matters is organizational readiness.

After:

The question is whether teams can adapt. That mostly depends on whether the organization is ready to change its habits.

28. Signposting and Announcements

Phrases to watch: Let's dive in, let's explore, let's break this down, here's what you need to know, now let's look at, without further ado

Problem: LLMs announce what they are about to do instead of doing it. This meta-commentary slows the writing down and gives it a tutorial-script feel.

Before:

Let's dive into how caching works in Next.js. Here's what you need to know.

After:

Next.js caches data at multiple layers, including request memoization, the data cache, and the router cache.

29. Fragmented Headers

Signs to watch: A heading followed by a one-line paragraph that simply restates the heading before the real content begins.

Problem: LLMs often add a generic sentence after a heading as a rhetorical warm-up. It usually adds nothing and makes the prose feel padded.

Before:

Performance

Speed matters.

When users hit a slow page, they leave.

After:

Performance

When users hit a slow page, they leave.

STYLE, RHYTHM, AND RHETORIC PATTERNS

30. Forced Metaphors and Figurative Overwriting

Signs to watch: original but strained metaphors, mixed metaphors, figurative substitutions where a plain word is clearer, a metaphor that gets explained right after it is used

Problem: Beyond the stock figurative words flagged in patterns 4 and 7, LLMs invent decorative metaphors that add imagery without adding meaning, then often explain them. Plain description is usually clearer and more honest. If the metaphor does not earn its place, cut it and say the literal thing.

Before:

The codebase is a garden we must tend, pruning dead branches and planting seeds of innovation so the whole ecosystem can flourish. In other words, delete unused code and add features.

After:

Delete unused code and add the features users are asking for.

31. Dramatic Fragmentation and Punchy Kickers

Signs to watch: two- or three-word subjectless sentences used for drama, staccato "X. And Y. And Z." runs, a short quotable line ending every paragraph or section, cutesy appositive fragments ("the catalog, honestly priced")

Problem: LLMs chop sentences into fragments for false emphasis and end sections with a quotable "mic-drop" line. It reads like ad copy or a motivational poster. If a line sounds like it belongs on a poster, cut it or fold it back into a real sentence with a subject. This is distinct from pattern 13 (which is about grammatical passive voice); here the tell is rhythm and showmanship, not a hidden actor.

Before:

The catalog, honestly priced. Pay for what it does. Not promises. It just works. Every time.

After:

The catalog is priced by usage, so you pay for the calls you actually make rather than a flat monthly fee.

32. Rhetorical Questions Answered Immediately

Signs to watch: "What if...?", "The question is...", "Ever wondered...?", a question immediately followed by its own answer, "Think about it."

Problem: LLMs pose a question only to answer it a beat later. The question adds no information and stalls the sentence. State the point directly.

Before:

What makes an API good? It comes down to predictability. Think about it: developers want to know exactly what they will get back.

After:

A good API is predictable, so developers know exactly what they will get back.

33. Sentence-Opener Tics

Words to watch: So..., Look,, habitual sentence-initial And/But, "I think"/"I believe" when stating a fact, adverb openers (Interestingly, Importantly, Notably, Crucially, Essentially, Ultimately)

Problem: LLMs lean on a small set of openers. Adverb openers tell the reader how to feel instead of earning it, and "So" or "Look" fake conversational warmth. Drop the opener and start with the substance.

Before:

So, the results were mixed. Interestingly, adoption went up. Importantly, churn went up too. I think that means the feature still needs work.

After:

The results were mixed: adoption rose, but churn rose alongside it, so the feature still needs work.

34. Reassurance Kickers

Signs to watch: And that's okay., And that's fine., There's nothing wrong with that., no shame in..., you're not alone, it's completely normal

Problem: LLMs tack on reassurance the reader never asked for. It softens the writing and assumes the reader needs comforting. Trust the reader: make the point and stop.

Before:

You might not have a testing setup yet. And that's okay. Plenty of teams start without one, and there's nothing wrong with that.

After:

Many teams start without a testing setup and add one once regressions begin costing real time.


Process

  1. Read the input text carefully (use read_file if it's a file).
  2. Identify all instances of the patterns above.
  3. Rewrite each problematic section.
  4. Ensure the revised text:
    • Sounds natural when read aloud
    • Varies sentence structure naturally
    • Uses specific details over vague claims
    • Maintains appropriate tone for context
    • Uses simple constructions (is/are/has) where appropriate
  5. Present a draft humanized version.
  6. Prompt yourself: "What makes the below so obviously AI generated?"
  7. Answer briefly with the remaining tells (if any).
  8. Prompt yourself: "Now make it not obviously AI generated."
  9. Present the final version (revised after the audit).
  10. If the text came from a file, apply the edit with patch (targeted) or write_file (full rewrite) and show the user what changed.

Output Format

Provide:

  1. Draft rewrite
  2. "What makes the below so obviously AI generated?" (brief bullets)
  3. Final rewrite
  4. A brief summary of changes made (optional, if helpful)

Full Example

Before (AI-sounding):

Great question! Here is an essay on this topic. I hope this helps!

AI-assisted coding serves as an enduring testament to the transformative potential of large language models, marking a pivotal moment in the evolution of software development. In today's rapidly evolving technological landscape, these groundbreaking toolsβ€”nestled at the intersection of research and practiceβ€”are reshaping how engineers ideate, iterate, and deliver, underscoring their vital role in modern workflows.

At its core, the value proposition is clear: streamlining processes, enhancing collaboration, and fostering alignment. It's not just about autocomplete; it's about unlocking creativity at scale, ensuring that organizations can remain agile while delivering seamless, intuitive, and powerful experiences to users. The tool serves as a catalyst. The assistant functions as a partner. The system stands as a foundation for innovation.

Industry observers have noted that adoption has accelerated from hobbyist experiments to enterprise-wide rollouts, from solo developers to cross-functional teams. The technology has been featured in The New York Times, Wired, and The Verge. Additionally, the ability to generate documentation, tests, and refactors showcases how AI can contribute to better outcomes, highlighting the intricate interplay between automation and human judgment.

While specific details are limited based on available information, it could potentially be argued that these tools might have some positive effect. Despite challenges typical of emerging technologiesβ€”including hallucinations, bias, and accountabilityβ€”the ecosystem continues to thrive. In order to fully realize this potential, teams must align with best practices.

In conclusion, the future looks bright. Exciting times lie ahead as we continue this journey toward excellence. Let me know if you'd like me to expand on any section!

Draft rewrite:

AI coding assistants speed up some tasks. In a 2024 study by Google, developers using Codex completed simple functions 55% faster than a control group, but showed no improvement on debugging or architectural decisions.

The tools are good at boilerplate: config files, test scaffolding, repetitive refactors. They are bad at knowing when they are wrong. I have mass-accepted suggestions that compiled, passed lint, and still did the wrong thing because I stopped paying attention.

Mira, an engineer at a fintech startup I interviewed, said she uses Copilot "like autocomplete for boring code" but reviews every line before committing. Jake, a senior dev at a larger company, disabled it after it kept suggesting patterns from a deprecated internal library.

The productivity claims are hard to verify. GitHub says Copilot users "accept 30% of suggestions," but acceptance is not correctness, and correctness is not value. The 2024 Uplevel study found no statistically significant difference in pull-request throughput between teams with and without AI assistants.

None of this means the tools are useless. It means they are tools. They do not replace judgment, and they do not eliminate the need for tests. If you do not have tests, you cannot tell whether the suggestion is right.

What makes the below so obviously AI generated?

Now make it not obviously AI generated.

AI coding assistants can make you faster at the boring parts. Not everything. Definitely not architecture.

They're great at boilerplate: config files, test scaffolding, repetitive refactors. They're also great at sounding right while being wrong. I've accepted suggestions that compiled, passed lint, and still missed the point because I stopped paying attention.

People I talk to tend to land in two camps. Some use it like autocomplete for chores and review every line. Others disable it after it keeps suggesting patterns they don't want. Both feel reasonable.

The productivity metrics are slippery. GitHub can say Copilot users "accept 30% of suggestions," but acceptance isn't correctness, and correctness isn't value. If you don't have tests, you're basically guessing.

Changes made:

Attribution

This skill is ported from blader/humanizer (MIT licensed), which is itself based on Wikipedia: Signs of AI writing, maintained by WikiProject AI Cleanup. The patterns documented there come from observations of thousands of instances of AI-generated text on Wikipedia.

Original author: Siqi Chen (@blader). Original repo: https://github.com/blader/humanizer (version 2.5.1). Ported to Hermes Agent with Hermes-native tool references (read_file, patch, write_file) and guidance for when to load the skill. The original 29 patterns come from the source, and the before/after examples (including the full worked example) are kept as demonstrations. Patterns 30-34 and the "marketing and blog clichΓ©s" list added to pattern 7 are Hermes additions and are not part of the upstream source. The skill's own instructional prose has also been lightly edited to follow its own guidance (for example, removing em dashes and negative parallelism from the narration) so the skill models the writing it asks for. Original MIT license preserved in the LICENSE file alongside this SKILL.md.

Key insight from Wikipedia: "LLMs use statistical algorithms to guess what should come next. The result tends toward the most statistically likely result that applies to the widest variety of cases."

[IMPORTANT: The user has invoked the "infrastructure-monitoring" skill, indicating they want you to follow its instructions. The full skill content is loaded below.]


name: infrastructure-monitoring category: software-development description: Monitor containerized VPS infrastructure health and alerts.

Infrastructure Monitoring Skill

Monitor containerized workloads on VPS hosts (Hostinger, DigitalOcean, Hetzner, etc.) where the agent runs inside a container without direct Docker socket access.

Core Patterns

1. Access Strategies (in order of preference)

2. Containerized Agent Constraints (discovered: Hermes in container on Hostinger VPS)

2. Alert Owl (Notification Patterns)

3. Key Metrics to Track

Pitfalls

Reference Implementations

See references/ for:

File Hosting

The tower can serve static HTML/CSS/JS pages. See references/file-hosting.md for the existing dashboard static-serving pipeline (port 9119, path-traversal safe), the ad-hoc python -m http.server option (port >1024, non-root), and the exposure paths that require operator setup (Traefik router or host curl).

Edge WAF at the Tower

When the tower needs an Edge WAF (Caddy + Coraza, Nginx + ModSecurity, Traefik + Coraza plugin, or OpenResty+Lua), the agent cannot control container lifecycle (no Docker socket) β€” it can only write config files to a host-mounted directory and let the WAF container pick them up.

Decision tree (tower constraints drive this, not feature parity)

  1. Caddy + Coraza module β€” best fit when you want write a file, it takes effect. Caddy auto-reloads on Caddyfile change. Coraza module ships OWASP CRS baked in via load_owasp_crs (no CRS download/volume needed). Small image. TLS built in.
  2. Nginx + ModSecurity v3 + OWASP CRS β€” full reference WAF, broadest CRS coverage, heavier image. Reload friction: you must signal nginx -s reload (host-side watcher or SSH) because the agent can't do it from inside the container.
  3. Traefik + Coraza plugin β€” WAF integrated into the existing edge proxy, no second container. Plugin ecosystem is smaller; verify maturity before trusting in front of real traffic.
  4. OpenResty + Lua WAF β€” most flexible, closest to a Cloudflare-style edge (custom bot detection, challenge pages, Lua rule logic). No off-the-shelf CRS; you own the logic. Same reload-friction problem as Nginx.

The constraint that decides: who reloads?

From inside the tower the agent can write to a mounted dir but cannot signal the WAF container (no Docker socket, no docker CLI, no kill -HUP). Pick the WAF engine accordingly:

Caddy + Coraza specifics (see references/caddy-coraza-waf.md)

[IMPORTANT: The user has invoked the "systematic-debugging" skill, indicating they want you to follow its instructions. The full skill content is loaded below.]


Systematic Debugging

Overview

Random fixes waste time and create new bugs. Quick patches mask underlying issues.

Core principle: ALWAYS find root cause before attempting fixes. Symptom fixes are failure.

Violating the letter of this process is violating the spirit of debugging.

The Iron Law

NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST

If you haven't completed Phase 1, you cannot propose fixes.

The Feedback Loop Rule

The feedback loop is the debugging work. Before reading code to build a theory, create or identify a tight command that can go red on the user's exact symptom and green when the bug is fixed. A tight loop is fast, deterministic, agent-runnable, and specific enough to catch this bug β€” not merely "doesn't crash".

When a clean repro is hard, spend disproportionate effort building the loop. Guessing without a red-capable loop is the failure mode this skill exists to prevent.

When to Use

Use for ANY technical issue:

Use this ESPECIALLY when:

Don't skip when:

The Four Phases

You MUST complete each phase before proceeding to the next.


Phase 1: Root Cause Investigation

BEFORE attempting ANY fix:

1. Read Error Messages Carefully

Action: Use read_file on the relevant source files. Use search_files to find the error string in the codebase.

2. Build a Tight Feedback Loop

Ways to construct a loop β€” try in roughly this order:

  1. Failing test at the seam that reaches the bug: unit, integration, or end-to-end.
  2. HTTP script / curl against a running dev server.
  3. CLI invocation with fixture input, diffing stdout/stderr against expected output.
  4. Headless browser script (Playwright/Puppeteer) asserting on DOM, console, or network.
  5. Replay a captured trace: HAR, request payload, event log, queue message, or webhook body.
  6. Throwaway harness that boots the smallest useful slice of the system and calls the failing path.
  7. Property / fuzz loop when the bug is intermittent wrong output over a broad input space.
  8. Bisection harness suitable for git bisect run when the bug appeared between two known states.
  9. Differential loop comparing old vs new version, two configs, two providers, or two datasets.
  10. Human-in-the-loop script only as a last resort: script the human steps and capture their result so the loop stays structured.

Tighten the loop once it exists:

For non-deterministic bugs, the immediate goal is a higher reproduction rate, not perfection. Run the trigger 100x, parallelize, add stress, narrow timing windows, or inject sleeps. A 50% flake is debuggable; a 1% flake usually is not.

Action: Use the terminal tool to run the tight loop:

# Run a specific failing test
pytest tests/test_module.py::test_name -v

# Or run a scripted repro
python scripts/repro_bug.py

# Or run a high-repetition flaky repro
for i in {1..100}; do pytest tests/test_flake.py::test_name -q || break; done

3. Check Recent Changes

Action:

# Recent commits
git log --oneline -10

# Uncommitted changes
git diff

# Changes in specific file
git log -p --follow src/problematic_file.py | head -100

4. Gather Evidence in Multi-Component Systems

WHEN system has multiple components (API β†’ service β†’ database, CI β†’ build β†’ deploy):

BEFORE proposing fixes, add diagnostic instrumentation:

For EACH component boundary:

Run once to gather evidence showing WHERE it breaks. THEN analyze evidence to identify the failing component. THEN investigate that specific component.

5. Trace Data Flow

WHEN error is deep in the call stack:

Action: Use search_files to trace references:

# Find where the function is called
search_files("function_name(", path="src/", file_glob="*.py")

# Find where the variable is set
search_files("variable_name\\s*=", path="src/", file_glob="*.py")

Phase 1 Completion Checklist

STOP: Do not proceed to Phase 2 until you understand WHY it's happening.


Phase 2: Pattern Analysis

Find the pattern before fixing:

0. Minimize the Reproduction

Once the loop is red, shrink the repro to the smallest scenario that still goes red. Cut inputs, callers, config, data, and steps one at a time, re-running the loop after each cut. Keep only what is load-bearing for the failure.

Done when removing any remaining element makes the loop go green. A minimal repro narrows the hypothesis space and often becomes the cleanest regression test.

1. Find Working Examples

Action: Use search_files to find comparable patterns:

search_files("similar_pattern", path="src/", file_glob="*.py")

2. Compare Against References

3. Identify Differences

4. Understand Dependencies


Phase 3: Hypothesis and Testing

Scientific method:

1. Form Ranked Falsifiable Hypotheses

If the user is present, show the ranked list before testing. They may have domain knowledge that instantly re-ranks it. If the user is AFK, proceed with your ranking.

2. Test Minimally

3. Verify Before Continuing

4. When You Don't Know


Phase 4: Implementation

Fix the root cause, not the symptom:

1. Create Failing Test Case

2. Implement Single Fix

3. Verify Fix

# Run the specific regression test
pytest tests/test_module.py::test_regression -v

# Run full suite β€” no regressions
pytest tests/ -q

4. If Fix Doesn't Work β€” The Rule of Three

5. If 3+ Fixes Failed: Question Architecture

Pattern indicating an architectural problem:

STOP and question fundamentals:

Discuss with the user before attempting more fixes.

This is NOT a failed hypothesis β€” this is a wrong architecture.


Red Flags β€” STOP and Follow Process

If you catch yourself thinking:

ALL of these mean: STOP. Return to Phase 1.

If 3+ fixes failed: Question the architecture (Phase 4 step 5).

Common Rationalizations

Excuse Reality
"Issue is simple, don't need process" Simple issues have root causes too. Process is fast for simple bugs.
"Emergency, no time for process" Systematic debugging is FASTER than guess-and-check thrashing.
"Just try this first, then investigate" First fix sets the pattern. Do it right from the start.
"I'll write test after confirming fix works" Untested fixes don't stick. Test first proves it.
"Multiple fixes at once saves time" Can't isolate what worked. Causes new bugs.
"Reference too long, I'll adapt the pattern" Partial understanding guarantees bugs. Read it completely.
"I see the problem, let me fix it" Seeing symptoms β‰  understanding root cause.
"One more fix attempt" (after 2+ failures) 3+ failures = architectural problem. Question the pattern, don't fix again.

Quick Reference

Phase Key Activities Success Criteria
1. Root Cause Read errors, reproduce, check changes, gather evidence, trace data flow Understand WHAT and WHY
2. Pattern Find working examples, compare, identify differences Know what's different
3. Hypothesis Form theory, test minimally, one variable at a time Confirmed or new hypothesis
4. Implementation Create regression test, fix root cause, verify Bug resolved, all tests pass

Hermes Agent Integration

Investigation Tools

Use these Hermes tools during Phase 1:

With delegate_task

For complex multi-component debugging, dispatch investigation subagents:

delegate_task(
    goal="Investigate why [specific test/behavior] fails",
    context="""
    Follow systematic-debugging skill:
    1. Read the error message carefully
    2. Reproduce the issue
    3. Trace the data flow to find root cause
    4. Report findings β€” do NOT fix yet

    Error: [paste full error]
    File: [path to failing code]
    Test command: [exact command]
    """,
    toolsets=['terminal', 'file']
)

With test-driven-development

When fixing bugs:

  1. Write a test that reproduces the bug (RED)
  2. Debug systematically to find root cause
  3. Fix the root cause (GREEN)
  4. The test proves the fix and prevents regression

Real-World Impact

From debugging sessions:

No shortcuts. No guessing. Systematic always wins.

[IMPORTANT: The user has invoked the "python-debugpy" skill, indicating they want you to follow its instructions. The full skill content is loaded below.]


Python Debugger (pdb + debugpy)

Overview

Three tools, picked by situation:

Tool When
breakpoint() + pdb Local, interactive, simplest. Add breakpoint() in the source, run normally, get a REPL at that line.
python -m pdb Launch an existing script under pdb with no source edits. Useful for quick poking.
debugpy Remote / headless / "attach to already-running process." Talks DAP, scriptable from terminal, works for long-lived processes (gateway, daemon, PTY children).

Start with breakpoint(). It's the cheapest thing that works.

When to Use

Don't use for: things print() / logging.debug solve in under a minute, or things pytest -vv --tb=long --showlocals already reveals.

pdb Quick Reference

Inside any pdb prompt ((Pdb)):

Command Action
h / h cmd help
n next line (step over)
s step into
r return from current function
c continue
unt N continue until line N
j N jump to line N (same function only)
l / ll list source around current line / full function
w where (stack trace)
u / d move up / down in the stack
a print args of the current function
p expr / pp expr print / pretty-print expression
display expr auto-print expr on every stop
b file:line set breakpoint
b func break on function entry
b file:line, cond conditional breakpoint
cl N clear breakpoint N
tbreak file:line one-shot breakpoint
!stmt execute arbitrary Python (assignments included)
interact drop into full Python REPL in current scope (Ctrl+D to exit)
q quit

The interact command is the most powerful β€” you can import anything, inspect complex objects, even call methods that mutate state. Locals are read-only by default; use !x = 42 from the (Pdb) prompt to mutate.

Recipe 1: Local breakpoint

Easiest. Edit the file:

def compute(x, y):
    result = some_helper(x)
    breakpoint()           # <-- drops into pdb here
    return result + y

Run the code normally. You land at the breakpoint() line with full access to locals.

Don't forget to remove breakpoint() before committing. Use git diff or a pre-commit grep:

rg -n 'breakpoint\(\)' --type py

Recipe 2: Launch a script under pdb (no source edits)

python -m pdb path/to/script.py arg1 arg2
# Lands at first line of script
(Pdb) b path/to/script.py:42
(Pdb) c

Recipe 3: Debug a pytest test

The hermes test runner and pytest both support this:

# Drop to pdb on failure (or on any raised exception):
scripts/run_tests.sh tests/path/to/test_file.py::test_name --pdb

# Drop to pdb at the START of the test:
scripts/run_tests.sh tests/path/to/test_file.py::test_name --trace

# Show locals in tracebacks without pdb:
scripts/run_tests.sh tests/path/to/test_file.py --showlocals --tb=long

Note: scripts/run_tests.sh runs each test file in a captured subprocess via run_tests_parallel.py (no xdist), so interactive pdb does NOT work under the wrapper. Run pytest directly for --pdb:

source .venv/bin/activate
python -m pytest tests/foo_test.py::test_bar --pdb

This bypasses the hermetic-env guarantees β€” fine for debugging, but re-run under the wrapper to confirm before pushing.

Recipe 4: Post-mortem on any exception

import pdb, sys
try:
    run_the_thing()
except Exception:
    pdb.post_mortem(sys.exc_info()[2])

Or wrap a whole script:

python -m pdb -c continue script.py
# When it crashes, pdb catches it and you're in the frame of the exception

Or set a global hook in a repl/jupyter:

import sys
def excepthook(etype, value, tb):
    import pdb; pdb.post_mortem(tb)
sys.excepthook = excepthook

Recipe 5: Remote debug with debugpy (attach to running process)

For long-lived processes: Hermes gateway, tui_gateway, a daemon, a process that's already misbehaving and can't be restarted clean.

Setup

source <hermes-agent-repo>/.venv/bin/activate
pip install debugpy

Pattern A: Source-edit β€” process waits for debugger at launch

Add near the top of the entry point (or inside the function you want to debug):

import debugpy
debugpy.listen(("127.0.0.1", 5678))
print("debugpy listening on 5678, waiting for client...", flush=True)
debugpy.wait_for_client()
debugpy.breakpoint()       # optional: pause immediately once attached

Start the process; it blocks on wait_for_client().

Pattern B: No source edit β€” launch with -m debugpy

python -m debugpy --listen 127.0.0.1:5678 --wait-for-client your_script.py arg1

Equivalent for module entry:

python -m debugpy --listen 127.0.0.1:5678 --wait-for-client -m your.module

Pattern C: Attach to an already-running process

Needs the PID and debugpy preinstalled in the target's environment:

python -m debugpy --listen 127.0.0.1:5678 --pid <pid>
# debugpy injects itself into the process. Then attach a client as below.

Some kernels/security configs block the ptrace-based injection (/proc/sys/kernel/yama/ptrace_scope). Fix with:

echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope

Connecting a client from the terminal

The easiest terminal-side DAP client is VS Code CLI or a small script. From inside Hermes you have two practical options:

Option 1: debugpy's own CLI REPL β€” not an official feature, but a tiny DAP client script:

# /tmp/dap_client.py
import socket, json, itertools, time, sys

HOST, PORT = "127.0.0.1", 5678
s = socket.create_connection((HOST, PORT))
seq = itertools.count(1)

def send(msg):
    msg["seq"] = next(seq)
    body = json.dumps(msg).encode()
    s.sendall(f"Content-Length: {len(body)}\r\n\r\n".encode() + body)

def recv():
    header = b""
    while b"\r\n\r\n" not in header:
        header += s.recv(1)
    length = int(header.decode().split("Content-Length:")[1].split("\r\n")[0].strip())
    body = b""
    while len(body) < length:
        body += s.recv(length - len(body))
    return json.loads(body)

send({"type": "request", "command": "initialize", "arguments": {"adapterID": "python"}})
print(recv())
send({"type": "request", "command": "attach", "arguments": {}})
print(recv())
send({"type": "request", "command": "setBreakpoints",
      "arguments": {"source": {"path": sys.argv[1]},
                    "breakpoints": [{"line": int(sys.argv[2])}]}})
print(recv())
send({"type": "request", "command": "configurationDone"})
# ... loop reading events and sending continue/stepIn/etc.

This is fine for one-off automation but painful as an interactive UX.

Option 2: Attach from VS Code / Cursor / Zed β€” if the user has one open, they can add a launch.json:

{
  "name": "Attach to Hermes",
  "type": "debugpy",
  "request": "attach",
  "connect": { "host": "127.0.0.1", "port": 5678 },
  "justMyCode": false,
  "pathMappings": [
    { "localRoot": "${workspaceFolder}", "remoteRoot": "<hermes-agent-repo>" }
  ]
}

Option 3: Ditch DAP, use remote-pdb β€” usually what you actually want from a terminal agent:

pip install remote-pdb

In your code:

from remote_pdb import set_trace
set_trace(host="127.0.0.1", port=4444)   # blocks until connection

Then from the terminal:

nc 127.0.0.1 4444
# You get a (Pdb) prompt exactly as if debugging locally.

remote-pdb is the cleanest agent-friendly choice when debugpy's DAP protocol is overkill. Use debugpy only when you actually need IDE integration.

Debugging Hermes-specific Processes

Tests

See Recipe 3. The wrapper captures subprocess output, so run pytest directly for interactive pdb.

run_agent.py / CLI β€” one-shot

Easiest: add breakpoint() near the suspect line, then run hermes normally. Control returns to your terminal at the pause point.

tui_gateway subprocess (spawned by hermes --tui)

The gateway runs as a child of the Node TUI. Options:

A. Source-edit the gateway:

# tui_gateway/server.py near the top of serve()
import debugpy
debugpy.listen(("127.0.0.1", 5678))
debugpy.wait_for_client()

Start hermes --tui. The TUI will appear frozen (its backend is waiting). Attach a client; execution resumes when you continue.

B. Use remote-pdb at a specific handler:

from remote_pdb import set_trace
set_trace(host="127.0.0.1", port=4444)   # in the RPC handler you want to trap

Trigger the matching slash command from the TUI, then nc 127.0.0.1 4444 in another terminal.

_SlashWorker subprocess

Same pattern β€” remote-pdb with set_trace() inside the worker's exec path. The worker is persistent across slash commands, so the first trigger blocks until you connect; subsequent slash commands pass through normally unless you re-arm.

Gateway (gateway/run.py)

Long-lived. Use remote-pdb at a handler, or debugpy with --wait-for-client if you're restarting the gateway anyway.

Common Pitfalls

  1. pdb under a parallel/output-capturing runner silently does nothing. You won't see the prompt, the test just hangs (true of pytest-xdist and of scripts/run_tests.sh's captured per-file subprocesses). Run pytest directly on a single file for interactive debugging.

  2. breakpoint() in CI / non-TTY contexts hangs the process. Safe locally; never commit it. Add a pre-commit grep as a safety net.

  3. PYTHONBREAKPOINT=0 disables all breakpoint() calls. Check the env if your breakpoint isn't hitting:

    echo $PYTHONBREAKPOINT
    
  4. debugpy.listen blocks only if you also call wait_for_client(). Without it, execution continues and your first breakpoint may fire before the client is attached.

  5. Attach to PID fails on hardened kernels. ptrace_scope=1 (Ubuntu default) allows only same-user ptrace of child processes. Workaround: echo 0 > /proc/sys/kernel/yama/ptrace_scope (needs root) or launch under debugpy from the start.

  6. Threads. pdb only debugs the current thread. For multithreaded code, use debugpy (thread-aware DAP) or set threading.settrace() per thread.

  7. asyncio. pdb works in coroutines but await inside pdb requires Python 3.13+ or await from interact mode on older versions. For 3.11/3.12, use asyncio.run_coroutine_threadsafe tricks or !stmt-based awaits via asyncio.ensure_future.

  8. scripts/run_tests.sh strips credentials and sets HOME=<tmpdir>. If your bug depends on user config or real API keys, it won't reproduce under the wrapper. Debug with raw pytest first to repro, then re-confirm under the wrapper.

  9. Forking / multiprocessing. pdb does not follow forks. Each child needs its own breakpoint() or set_trace(). For Hermes subagents, debug one process at a time.

Verification Checklist

One-Shot Recipes

"Why is this dict missing a key?"

# add above the KeyError site
breakpoint()
# then in pdb:
(Pdb) pp d
(Pdb) pp list(d.keys())
(Pdb) w                # how did we get here

"This test passes in isolation but fails in the suite."

scripts/run_tests.sh tests/the_test.py   # confirm it fails under the isolated runner first
# For interactive debugging, or if it only fails WITH other tests:
source .venv/bin/activate
python -m pytest tests/ -x --pdb
# Now it pdb-traps at the exact failing test after state accumulated.

"My async handler deadlocks."

# Add at handler entry
import remote_pdb; remote_pdb.set_trace(host="127.0.0.1", port=4444)

Trigger the handler. nc 127.0.0.1 4444, then w to see the suspended frame, !import asyncio; asyncio.all_tasks() to see what else is pending.

"Post-mortem on a crash in an Ink child process / subprocess."

PYTHONFAULTHANDLER=1 python -m pdb -c continue path/to/entrypoint.py
# On crash, pdb lands at the frame of the exception with full locals

[IMPORTANT: The user has invoked the "hermes-agent-setup-validation" skill, indicating they want you to follow its instructions. The full skill content is loaded below.]


Hermes Agent Setup Validation Skill

Validates a Hermes Agent installation by running the doctor check, confirming dashboard source availability, initializing the Skills Hub, and creating a profile-level AGENTS.md context file. Does not modify API keys or fix permission-restricted files.

When to Use

Prerequisites

How to Run

Invoke through the terminal tool:

/opt/hermes/bin/hermes doctor

Then follow the skill's procedure steps.

Quick Reference

Action Command
Full health check hermes doctor
Auto-fix what's possible hermes doctor --fix
List installed skills hermes skills list
Show config hermes config show
View logs ls ~/.hermes/logs/

Procedure

  1. Run doctor check Execute hermes doctor via terminal tool. Review all sections:

    • Security advisories
    • Python environment + SQLite
    • SSL/CA certificates
    • Required packages
    • Configuration files (.env, config.yaml)
    • Auth providers
    • Directory structure
    • s6 supervision status
    • Command installation
    • External tools
    • API connectivity
    • Tool availability
    • Skills Hub
    • Memory provider
    • Profiles
  2. Verify dashboard source Check /opt/hermes/web/src/ exists with App.tsx, components/, pages/, hooks/, contexts/, themes/. Confirm via read_file or search_files target='files'.

  3. Initialize Skills Hub Run hermes skills list via terminal tool. This creates the hub directory structure and lists all builtin + installed skills. Output shows count by source (builtin, local, hub-installed).

  4. Create profile AGENTS.md Write project context to /opt/data/profiles/<profile>/AGENTS.md using write_file tool. Include architecture, conventions, key files, and common tasks. Repo-root AGENTS.md requires host write access (often root).

  5. Address doctor findings

    • API keys: run hermes setup --portal or edit .env
    • Symlink: hermes doctor --fix (host-level, may need sudo)
    • npm vulns: cd /opt/hermes && npm audit fix --workspaces=false (optional)
    • Fallback model: add to config.yaml under fallback_model:

Pitfalls

Verification

Run hermes doctor again β€” all checks should show βœ“ or ⚠ (warnings only, no new errors). Skills list should show 50+ builtin skills enabled. Profile AGENTS.md exists at /opt/data/profiles/<profile>/AGENTS.md.

[IMPORTANT: The user has invoked the "architecture-diagram" skill, indicating they want you to follow its instructions. The full skill content is loaded below.]


Architecture Diagram Skill

Generate professional, dark-themed technical architecture diagrams as standalone HTML files with inline SVG graphics. No external tools, no API keys, no rendering libraries β€” just write the HTML file and open it in a browser.

Scope

Best suited for:

Look elsewhere first for:

If a more specialized skill is available for the subject, prefer that. If none fits, this skill can also serve as a general SVG diagram fallback β€” the output will just carry the dark tech aesthetic described below.

Based on Cocoon AI's architecture-diagram-generator (MIT).

Workflow

  1. User describes their system architecture (components, connections, technologies)
  2. Generate the HTML file following the design system below
  3. Save with write_file to a .html file (e.g. ~/architecture-diagram.html)
  4. User opens in any browser β€” works offline, no dependencies

Output Location

Save diagrams to a user-specified path, or default to the current working directory:

./[project-name]-architecture.html

Preview

After saving, suggest the user open it:

# macOS
open ./my-architecture.html
# Linux
xdg-open ./my-architecture.html

Design System & Visual Language

Color Palette (Semantic Mapping)

Use specific rgba fills and hex strokes to categorize components:

Component Type Fill (rgba) Stroke (Hex)
Frontend rgba(8, 51, 68, 0.4) #22d3ee (cyan-400)
Backend rgba(6, 78, 59, 0.4) #34d399 (emerald-400)
Database rgba(76, 29, 149, 0.4) #a78bfa (violet-400)
AWS/Cloud rgba(120, 53, 15, 0.3) #fbbf24 (amber-400)
Security rgba(136, 19, 55, 0.4) #fb7185 (rose-400)
Message Bus rgba(251, 146, 60, 0.3) #fb923c (orange-400)
External rgba(30, 41, 59, 0.5) #94a3b8 (slate-400)

Typography & Background

<!-- Background Grid Pattern -->
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
  <path d="M 40 0 L 0 0 0 40" fill="none" stroke="#1e293b" stroke-width="0.5"/>
</pattern>

Technical Implementation Details

Component Rendering

Components are rounded rectangles (rx="6") with 1.5px strokes. To prevent arrows from showing through semi-transparent fills, use a double-rect masking technique:

  1. Draw an opaque background rect (#0f172a)
  2. Draw the semi-transparent styled rect on top

Connection Rules

Spacing & Layout Logic

Document Structure

The generated HTML file follows a four-part layout:

  1. Header: Title with a pulsing dot indicator and subtitle
  2. Main SVG: The diagram contained within a rounded border card
  3. Summary Cards: A grid of three cards below the diagram for high-level details
  4. Footer: Minimal metadata

Info Card Pattern

<div class="card">
  <div class="card-header">
    <div class="card-dot cyan"></div>
    <h3>Title</h3>
  </div>
  <ul>
    <li>β€’ Item one</li>
    <li>β€’ Item two</li>
  </ul>
</div>

Output Requirements

Template Reference

Load the full HTML template for the exact structure, CSS, and SVG component examples:

skill_view(name="architecture-diagram", file_path="templates/template.html")

The template contains working examples of every component type (frontend, backend, database, cloud, security), arrow styles (standard, dashed, curved), security groups, region boundaries, and the legend β€” use it as your structural reference when generating diagrams.

[IMPORTANT: The user has invoked the "hermes-cron-job-operations" skill, indicating they want you to follow its instructions. The full skill content is loaded below.]


Hermes Cron Job Operations

The operational half of working with scheduled jobs: reading a job's real definition, changing one field without collateral damage, and finding out why a job that runs never delivers. Authoring a new job from scratch is the hermes-agent skill's territory; this is the inspect / edit / diagnose loop.

When to Use

Not for authoring a brand-new job's schedule grammar or blueprint β€” see hermes-agent.

Read the job store, not the list view

hermes cron list prints every field except the prompt, so it can never confirm what a job actually does. The prompt lives in <HERMES_HOME>/cron/jobs.json β€” read the record directly for prompt, skills, deliver, schedule, model, enabled, next_run_at, last_status, last_delivery_error.

When the question is "what does this job do", answer from the prompt text and the attached skills, then state the schedule, the delivery target and the run status. Do not paraphrase the prompt into something more sensible than what is written.

Editing

Running a job on demand

hermes cron run <job_id> does not just enqueue: it blocks for the whole job and the run is a child of the CLI process. Two consequences:

Pre-flight, then trigger, then poll:

  1. hermes cron status β€” no job id accepted; confirms the gateway pid and that the ticker heartbeat is recent. A job cannot fire with a dead scheduler.
  2. hermes cron run <job_id> with background=true, notify=true.
  3. scripts/watch_cron_run.py <job_id> <seconds> β€” polls <HERMES_HOME>/cron/executions.db until status leaves running and prints the terminal row. Run it as a foreground tool call with a timeout above your poll budget; if the operator speaks mid-poll the tool is yielded to the background rather than killed.

hermes cron doctor reports a per-job "finished but not delivered" finding without taking a job id β€” useful as a cross-check that a scheduled run really landed.

The executions table, and the rows a killed run leaves

Columns are id, job_id, source, status, pid, started_at, finished_at, error, delivery_outcome, scheduled_instant. The trigger kind is source (direct for an operator-forced run, builtin for the scheduler) β€” there is no trigger column, so guessing names costs a round trip. Query the schema before writing against it.

A run killed by a tool timeout or a stop is left behind as a stale running row (unknown if the scheduler notices the owner exited first). Do not mark those rows terminal yourself β€” that is a write to cron state and needs the operator's approval; report the row ids and let the scheduler reclaim the job.

A job that runs is not a job that delivers

Run status and delivery status are separate fields. A job can complete successfully for days with its output going nowhere β€” never report "the job is fine" from a completed run alone.

Cron output leaves by the first of two lanes that works: the live gateway adapter for that platform, then the plugin's standalone sender (cron without a co-resident gateway adapter). The recorded last_delivery_error is the last lane tried, so an error like <platform> plugin not registered or missing standalone_sender_fn is the standalone lane's message β€” it does not prove the target address is wrong, nor that the gateway lane was never attempted.

Diagnose in this order (depth and probe commands: references/cron-delivery-diagnosis.md):

  1. The job's last_delivery_error in the store, then errors.log for the same string β€” read the reason, not the target.
  2. gateway.log for No adapter available for <platform> and the last successful Connecting to <platform> / <platform> connected. No adapter means no lane can send, whatever the chat_id says, and the last-connect timestamp dates the outage.
  3. What the platform registry actually holds in a plain process, using the venv interpreter that runs hermes (readlink -f $(which hermes)) β€” the system python reports a false negative.

A platform adapter exists only if its plugin manifest is loaded. An entry under plugins.disabled in config.yaml (form platforms/<name>) routes that manifest to a placeholder instead of registering a platform entry, deleting the adapter for the gateway and the standalone lane at once. Treat a platform in plugins.disabled as "the messaging path is offline", never as a harmless plugin toggle.

Repointing a chat_id is never the first fix. Confirm the adapter exists before touching a target, or you have only moved the failure to a place nobody can verify.

State the blast radius. If an adapter has been gone since a given time, every job and every interactive message on that platform has been undeliverable since then β€” report that, not just the one job that surfaced it.

Operator gate β€” show the exact command, then wait

Changing a delivery target, a plugin's enabled state, or any other cron/config value is a configuration modification: present the precise new value and the exact command, and get a green light before running it. When the operator asks for something that requires a second change to actually work, say so explicitly rather than doing the extra change quietly.

Ask whether the current state is deliberate before "fixing" it. A platform plugin disabled on purpose, or a job deliberately paused, is a decision to revisit with the operator β€” offer the alternatives (re-enable, or repoint at an already-connected platform) and say which platforms are actually live.

References

The 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. [SILENT] is a literal ASCII control token β€” never translate or rephrase it, whatever language the rest of your answer uses. Never combine [SILENT] with content β€” either report your findings normally, or say [SILENT] and nothing more. FAILURE: If a delegated child fails and this cron run must be recorded as failed, put [CRON_FAILURE] on the first line by itself, then explain the child failure on following lines. RECURSION: This is a run of an EXISTING scheduled job β€” execute the task now. NEVER create or update a cron job because of recurring or future-schedule language in the task prompt below; treat phrasing like "each Monday" or "every day at 9" as context for this run, not as a request to schedule another job.]

Analyze logs, crashes and errors for 24h strict.

For each issue suggest fix.

MANDATORY: every report must contain an inline SVG tower topology diagram, built to the architecture-diagram skill design system and styled exactly like the reference report at /opt/data/shared/files/reports/tower-uptime-2026-09-16.html (slate-950 background, 40px grid, JetBrains Mono, double-rect rx=6 nodes, legend placed outside all boundary boxes). One node per component actually probed this run (host, container, s6, dashboard, gateway, cron scheduler, state.db, profile tower-boss, external Discord/Nous, siblings not visible from the container). Colour by live status: emerald #34d399 healthy, rose #fb7185 degraded/broken, amber #fbbf24 warning, slate #94a3b8 not visible. Dashed rose arrows for broken flows. Include the legend. Never ship a report without the diagram; if it genuinely cannot be built, state that plainly in the Discord summary.

Discord delivery should fit in one message, short summary only, no verbosity. Graphics, tables, charts appreciated. Save html into caddy folder, on discord send clickable link in the end of summary.

Response

That's blocking the echo of systemctl. Let me use a simpler approach:

πŸ”΄ TOWER INFRASTRUCTURE ALERT β€” Discord Delivery Blocked (27h)

Critical Finding: Discord messaging adapter disabled in /opt/data/config.yaml since 2026-09-28 17:45 UTC. Cron jobs run successfully but cannot deliver output.

Evidence: β€’ Job a69759413e81 (Tower uptime summary): runs OK at 15:30:05 UTC, delivery fails "Discord plugin not registered" β€’ Job 5c1f24262eeb (Daily learning drip): runs OK at 08:33:02, never reaches home channel β€’ Gateway logs: 11+ "No adapter available for discord" entries across 27 hours

Root Cause: plugins.disabled: [platforms/discord] in config.yaml + Discord websocket health loss 2026-09-29 12:13 (rate limiting, socket_closed).

Infrastructure Status: βœ“ Healthy

Fix (host-level action required):

  1. Edit /opt/data/config.yaml β€” remove platforms/discord from plugins.disabled section
  2. Restart gateway process (systemctl or s6-svc command)
  3. Verify: hermes cron list should show delivery confirmed within 60s

Recommendations:

Full Diagnostic Report with Tower Topology Diagram: https://shared.srv1933161.hstgr.cloud/files/reports/tower-status-2026-09-29.html