LEGION_
Legion field intelligence · 2026-07 — 2026-08

How competitor monitoring works

A competitor watch is a recurring row in the Olympus scheduler table. On its cron tick the scheduler hands the stored prompt to a named agent persona, the agent fetches the competitor's public pages, writes a dated snapshot to a persistent volume, diffs it against the previous snapshot, and reports the difference to a Slack destination. There is no separate monitoring service; the whole system is one table, one poller, and one durable directory.

The loop, step by step

01

The watch is a scheduler row

Each watch is one row in olympus.scheduled_tasks holding the full instruction prompt, a cron_pattern such as 0 13 * * 1 for Mondays at 13:00 UTC, and the persona that should run it. Cancelled watches stay in the table with state cancelled, so always confirm which row is pending before assuming a schedule is live.

02

The scheduler service ticks

A dedicated scheduler container polls the table on a loop and runs scripts/run-scheduler-once.ts. It is gated by SCHEDULER_DISABLED and only starts under the compose profile that includes it, so a lean local stack will never fire a watch.

03

A persona executes the prompt

The due row is dispatched to a Hermes worker running the named persona. The prompt is the entire specification: which URLs to fetch, where to store snapshots, what counts as a material change, and what to escalate. Nothing about the watch lives in code, so editing a watch means editing its prompt.

04

Pages are fetched and a dated snapshot is written

The agent fetches the listed pages with its web extraction tool and writes a markdown snapshot named snapshot-YYYY-MM-DD.md into the watch directory on the shared hermes-homes Docker volume.

05

The new snapshot is diffed against the previous one

If no snapshot exists the run captures a baseline and stops. Otherwise it compares against the newest file and reports only material differences: pricing, packaging, product surface, integrations, positioning, and proof points.

06

Material changes are reported and escalated

The digest goes to the configured Slack destination. Each watch also carries an explicit escalation condition, the single change that would alter Legion's competitive answer, which the agent is told to flag loudly in the week it happens.

Prerequisites before a watch goes live

RequirementWhy it mattersHow to verify
A running scheduler serviceThe Olympus stack must be up with the scheduler container running and SCHEDULER_DISABLED unset. On a lean local stack the scheduler is not started and no watch will ever fire.docker compose ps scheduler
A persona that can reach the webThe watch must name a persona whose profile includes a web extraction tool. Hephaestus and Iris both qualify. A persona without web tooling will fire and produce nothing.olympus profiles
A persistent snapshot directorySnapshots must be written under the hermes-homes volume, mounted in every worker at /data/hermes-homes/<workspace-id>/competitor-watch/<slug>/. This is the single most common failure: paths like /root or /tmp are inside the worker container and are destroyed on every redeploy, which silently resets the baseline and means no diff is ever produced.docker compose exec worker-1 ls /data/hermes-homes/<workspace-id>/competitor-watch/
A Slack destinationWithout a configured Slack channel or thread the digest is written only to the scheduler log, where nobody reads it.Check HERMES_SLACK_CHANNEL is set for the stack.
Confirmed live URLsVerify every URL in the prompt returns HTTP 200 before the watch goes live. A watch pointed at a domain that does not serve will still run, still write snapshots, and still report, producing confident output about nothing.curl -s -o /dev/null -w '%{http_code}' https://<competitor-domain>/

Known failure modes

SymptomCauseFix
Every run reports a fresh baseline and no diff ever appears.The snapshot directory is a container-local path, so it is wiped on redeploy.Pin the absolute /data/hermes-homes/<workspace-id>/... path in the prompt and confirm the files persist after a deploy.
A watch appears scheduled but nothing fires.The row is in state cancelled, or a duplicate pending row with the same cron replaced it, or the scheduler container is not running.List the table and confirm exactly one pending row per watch, then confirm the scheduler service is up.
The record cites a domain that does not resolve or serve.A URL was transcribed from memory rather than verified against the live site.Curl every source URL when the watch is created and again whenever the record is republished.
Historical context is missing from a report.The Wayback CDX and availability endpoints intermittently return HTTP 503 and 429.Record the retrieval failure as a caveat. Absence of a snapshot is not evidence that a page did not exist.
A real change is captured but never surfaced to anyone.The weekly digest summarises headline movement and can pass over a large change in a secondary line item.Give the watch an explicit numeric-drift instruction so any figure that moves by more than half is called out on its own.

Add a company to the watch

To add a company to the watch, paste this into Slack addressed to Iris, replacing the bracketed values. It creates one recurring watch with a durable baseline and an explicit escalation condition.

@Iris create a weekly competitor watch for [COMPANY] at [https://example.com/].

Monitor these pages every Monday at 13:00 UTC: homepage, pricing, integrations, product, docs index, changelog or blog, and customer stories. Before the first run, confirm every URL returns HTTP 200 and tell me if any does not.

Store snapshots at /data/hermes-homes/<workspace-id>/competitor-watch/[slug]/snapshot-YYYY-MM-DD.md on the persistent volume. Never write them to /root or /tmp; those are inside the worker container and are wiped on every redeploy.

On the first run, capture a baseline and stop. On later runs, diff against the newest snapshot and report only material changes to pricing, packaging, product surface, integrations, positioning, or proof points. If a published figure moves by more than 50 percent in either direction, call it out on its own line even if nothing else changed.

Escalate loudly if [THE ONE CHANGE THAT WOULD ALTER LEGION'S COMPETITIVE ANSWER, for example: they ship owned sending infrastructure, or they launch a managed done-for-you service tier].

Cite a URL and a capture date for every claim. Keep vendor claims and LeadGrow analysis clearly separate. If a page cannot be retrieved, say so rather than inferring what it said. Report to this thread.

Once created, confirm the watch is live by asking Iris to list scheduled tasks and checking there is exactly one pending row for the new company with the cron pattern you expect.

Weekly monitoring loop

Each week, an analyst compares Oxygen’s public product, pricing, integration, and positioning pages with the last dated record. A meaningful difference is captured with its source URL and observation date, then written as vendor evidence; inference remains labelled as LeadGrow assessment.

01 · Detect

Compare public signals

Review the same first-party pages and flag changed copy, offers, integrations, pricing, or positioning.

02 · Record

Preserve the evidence

Save the exact claim beside its URL, capture date, publisher, and caveats so readers can retrace it.

03 · Escalate

Route material changes

Escalate changes that alter commercial positioning, buyer access, pricing, or product scope for Legion review; retain lower-signal changes in the next weekly record.

What this system does not yet do