# Geo Loop

> Run one bounded eGEOagents loop iteration over a workspace domain - read the charter and fresh collector data, do ONE unit of work, write substrate artifacts, append one Timeline entry and one LOG line. Use for loop mode, /geo:loop, scheduled GEO runs, or continuous monitoring.

- Skill: `mverab/geo-loop` (Agent Skill)
- Install (CLI): `npx skillmds@latest add mverab/geo-loop`
- Raw SKILL.md: https://api.skillmd.com/api/skills/mverab/geo-loop/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: mverab (https://skillmd.com/u/mverab)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/mverab/geo-loop

---


# GEO Loop Skill

One wake-up = **one bounded unit of work**. This skill governs loop mode only;
one-shot `/geo <url>` is unchanged and needs no workspace.

## Non-negotiables

1. **Never write to the eGEOagents repo tree.** Every write goes to the
   workspace resolved from `$EGEO_HOME` (default `~/.egeo/`).
2. **One unit of work per run.** Take the domain's `## Current focus`, or the
   first Backlog item if focus is already satisfied. Do not batch.
3. **Exactly one Timeline entry and exactly one LOG.md line per run.**
4. **Write, then verify.** Re-read every file you wrote before exiting.
5. **Never fabricate.** Rankings, competitors, and metrics come from collector
   JSONL under `data/` or from a tool call in this session. A claim you cannot
   cite is `confidence: low` with the uncertainty stated in the body.
6. **Append-only history.** Never edit or delete an existing Timeline entry or
   LOG line.

## Procedure

### 1. Resolve the plan

```bash
egeo loop run <domain> --dry-run
```

This is the source of truth for the run: it prints the charter's current focus,
the backlog, collector records added since the last Timeline entry, and
candidate signals. It writes nothing. Use `--json` when you want to parse it.

If it exits non-zero, stop and report — the workspace or the domain charter is
not in a runnable state (`egeo loop doctor` explains why).

### 2. Read the state

- `$EGEO_HOME/domains/<domain>/README.md` — charter, cadence, focus, backlog,
  Timeline (the last entry tells you where the previous run stopped).
- `$EGEO_HOME/project.yaml` — when present, the validated project identity,
  canonical domain, tracked queries/pages, and anti-spam/canibalization
  guardrails. `egeo loop doctor` is the source of truth for its validity.
- `$EGEO_HOME/data/<collector>/*.jsonl` — fresh ground truth.
- `$EGEO_HOME/signals/` and `$EGEO_HOME/docs/` — what is already known, so you
  dedupe instead of duplicating.
- `$EGEO_HOME/config.yaml` — models, budgets, `reflect.auto_apply`.
- `$EGEO_HOME/data/outcomes/ledger.jsonl` — append-only decision outcomes when `egeo loop decide` has run.

### 3. Do the work

Pick the single unit of work and execute it with the existing GEO capability —
the `content-scoring`, `competitive-analysis`, and `schema-generator` skills and
the `geo-*` agents all apply unchanged. Typical units:

| Situation in the plan | Unit of work |
|---|---|
| A target URL moved ≥3 positions | Write/update a signal explaining the move |
| Target absent from top 10 repeatedly | Signal + a doc proposing the fix |
| A tracked page's `content_hash` changed | Signal describing what changed |
| A tracked page has no JSON-LD | Doc with copy-paste JSON-LD for that page |
| No fresh data at all | Outcome `no-op` — do not invent work |

Ranking positions from `data/serp/` are ground truth. Any *simulated* ranking
you produce yourself is `confidence: low` and must say so in the body.

### 4. Write artifacts (SUBSTRATE.md)

`SUBSTRATE.md` in the repo root is the binding contract. In short:

- `signals/<slug>.md` — evidence. `docs/<slug>.md` — durable knowledge.
  Kebab-case filenames, English content, `kind` decides the folder.
- Frontmatter on line 1: `kind`, `domains` (existing dirs under `domains/`),
  `created`, `updated`, `confidence`, `sources`, plus `frequency` on signals.
  `sources` may be empty only when `confidence: low`.
- **Dedupe (§4):** re-observed evidence does **not** get a new file. On the
  existing signal, increment `frequency`, set `updated` to today, and append one
  dated Timeline entry.
- Body above `## Timeline` = what is true now (rewrite freely).
  `## Timeline` = append-only, newest at the bottom.
- Docs cite signals and repo paths; signals cite collector JSONL lines
  (`data/serp/example-com.jsonl#L42`).

### 5. Close the run

Append to `$EGEO_HOME/domains/<domain>/README.md`, at the very bottom:

```markdown
### 2026-07-24 run

Checked serp deltas for example.com; pricing page dropped 3 → 7 for
"best geo tool". Wrote signals/example-com-pricing-drop.md.

Outcome: success
```

`Outcome:` is the last line and one of `success`, `partial`, `failure`, `no-op`.
Then append exactly one line to `$EGEO_HOME/LOG.md`:

```
2026-07-24T18:20Z [example-com] run: wrote signals/example-com-pricing-drop.md, outcome=success
```

Grammar (SUBSTRATE.md §7): `<YYYY-MM-DDTHH:MMZ> [<domain>] <event>: <summary>`,
UTC minute precision, appended at the bottom, summary ending in
`outcome=<class>`.

Keep `## Current focus` accurate: if the focus item is done, promote the next
Backlog item and remove it from the Backlog.

### 6. Verify, then exit

```bash
python -m egeo.substrate_lint            # defaults to $EGEO_HOME
egeo loop doctor
```

Re-read the domain README and the tail of `LOG.md` to confirm your two
appends landed exactly once. Fix violations before exiting; a run that leaves
the substrate invalid is `Outcome: failure`.

Report to the user in three lines: unit of work, artifacts written, outcome.

## Collectors

Collectors are deterministic and LLM-free; run them before a loop run when the
plan shows no fresh data:

```bash
egeo loop collect serp --query "best geo tool" --target-domain example.com
egeo loop collect page --url https://example.com/pricing
```

`serp` needs `BRAVE_API_KEY`; both honour the daily budgets in `config.yaml` and
fail loudly rather than writing partial records. See `collectors/README.md` for
the contract.

## Common mistakes

- Writing into the repo `prompts/`, `geo-output/`, or `signals/` instead of
  `$EGEO_HOME` — loop state never lives in the repo.
- Two Timeline entries (or two LOG lines) for one run.
- A new signal file for evidence that already has one (use `frequency`).
- Claiming a competitor ranking without a `data/serp/` line to cite.
- Doing three units of work because the data looked interesting. One.

