Loop template

tested 2026-07-07 ✓catalog data (JSON)

Research Watch Loop

Monitor trusted sources and turn new, high-signal research changes into cited findings reports and optional follow-up tasks.

Human guide

Raw README.md

Research Watch Loop

The research-watch loop monitors trusted sources on a schedule, writes a cited findings report, records duplicate-suppression state, and optionally routes high-signal findings into a pull request or kanban task.

This directory is a draft starter template. "Draft" is registry/site operational metadata for the template directory, not a hidden loop.yaml field; the manifest itself uses only the fields defined by spec/loop-spec-v0.1.md. The template is not running unattended just because these files exist or have been copied; an installed loop starts only after an operator customizes sources, paths, schedule, credentials, and enables an approved backend.

Files

  • loop.yaml — agent-readable manifest for the template.
  • AGENT-INSTALL.md — exact install commands for cron, GitHub Actions, and agent-kanban backends.
  • scripts/research_watch.py — concrete standard-library runner used by all backends.
  • examples/sources.json — editable source list for a real run.
  • examples/dry-run-sources.json — local-only source list used by verification.
  • examples/github-actions/research-watch.yml — ready-to-copy workflow.
  • verification/dry-read-ambiguity-checklist.md — independent dry-read checklist for this revision.

The seven human questions

1. What loop do I need?

Use this loop when a project needs recurring awareness of research, engineering posts, repositories, or source files, and the desired output is a dated findings report plus optional follow-up work. It matches the watchdog / monitor pattern in docs/research/loop-landscape.md §2 and the research-watch template recommendation in §6.1.

2. What can it safely automate?

The manifest permits reading web/repo/file sources, writing a report, writing a state file, creating a proposal branch, and creating a follow-up task. It forbids public publishing, spending money, deleting user data, and mutating source systems. These claims map to permissions.allowed_actions and permissions.forbidden_actions in loop.yaml.

3. What do I need to connect?

Minimum setup needs only this repository, Python 3, a JSON source list, an output directory, and a state path. GitHub Actions PR mode also needs the built-in GITHUB_TOKEN permissions declared in the workflow. Agent-kanban mode needs a configured task board or Hermes kanban installation. The default manifest does not require a web-search API key and does not include a web_search source.

4. What can go wrong?

Fetches can fail, sources can become stale, the same item can be reported twice if state is lost, and an agent can over-interpret weak evidence. The runner records fetch errors in the report, the state path is declared in state.paths, duplicate suppression is a verification requirement, and public/product-direction changes are human-gated.

5. How do I know it worked?

A successful run prints RESEARCH_WATCH_REPORT=..., RESEARCH_WATCH_STATE=..., NEW_ITEMS=..., and FETCH_ERRORS=.... The report must contain source URLs or repo-relative file paths for every item, and the state file must include the report path and seen keys. The exact verification commands are in loop.yaml under verification.commands and in AGENT-INSTALL.md.

After copying the template into a target repository, run the concrete verification block in AGENT-INSTALL.md before enabling cron, GitHub Actions, or agent-kanban. The local dry-run proves the copied starter files execute; it does not approve live scheduling, public publishing, product-direction changes, or credential use.

6. How do I stop or roll it back?

For cron, remove the research_watch.py line from the user's crontab. For GitHub Actions, disable or delete .github/workflows/research-watch.yml. For agent-kanban, pause or remove the recurring task. Rollback is intentionally narrow: leave the previous state file unchanged when report generation fails; if a bad report PR is opened, close the PR or revert that report commit. This maps to failure_policy.rollback.

7. What should I improve after the first run?

Review the source list, prune noisy sources, add stronger sources, adjust cadence, and calibrate what qualifies as a follow-up task. Record maintenance evidence in the audit log path declared by maintenance.audit_log; the template's freshness cadence is weekly.

Backend behavior

All supported backends run the same helper and the same manifest validation command. Backend differences are installation and delivery only:

  • cron writes reports and state on the machine where cron runs.
  • github_actions writes reports/state on a branch and opens a pull request using peter-evans/create-pull-request.
  • agent_kanban creates a recurring agent task whose prompt runs the helper, reviews output, and creates follow-up cards only for high-signal findings.

Optional web-search sources

The default template avoids web-search credentials so the installed loop works with public RSS/URL/repo/file sources. If an operator later enables a web-search provider, they must update all three places together:

  1. add a web_search source to loop.yaml and to the runtime source JSON;
  2. add the provider's env var name to inputs.environment_variables and a matching credential scope to permissions.credential_scopes;
  3. persist the secret in the backend's supported secret store, such as a cron env file or GitHub Actions secret.

If no credential exists, do not add the web_search source, env var, or credential scope.

The job description (loop.yaml)

View raw loop.yaml

templates/research-watch/loop.yaml

spec_version: "0.1"
id: research-watch
name: Research Watch Loop
purpose: Monitor trusted sources and turn new, high-signal research changes into cited findings reports and optional follow-up tasks.
audience: [human, agent, operator]
trigger:
  type: schedule
  default: weekly
  schedule: "0 9 * * 1"
inputs:
  sources:
    - type: rss
      name: Practitioner and lab engineering feeds
      url: templates/research-watch/examples/sources.json
    - type: url
      name: Stable source URLs
      url: templates/research-watch/examples/sources.json
    - type: github_repo
      name: Agent framework repositories
      url: templates/research-watch/examples/sources.json
  files:
    - templates/research-watch/examples/sources.json
    - templates/research-watch/scripts/research_watch.py
capabilities:
  - polling
  - source_summarization
  - dedupe
  - cited_reporting
  - task_creation
permissions:
  allowed_actions:
    - read_web
    - read_repo
    - write_report
    - write_state
    - create_proposal_branch
    - create_task
  forbidden_actions:
    - publish_publicly_without_approval
    - spend_money
    - delete_user_data
    - mutate_source_systems
  credential_scopes:
    - repo:contents:write
    - pull_request:write
    - kanban:create
state:
  paths:
    - .loopmaster/state/research-watch.json
  retention: 90 days
outputs:
  artifacts:
    - docs/research/YYYY-MM-DD-research-watch.md
  notifications:
    - run_summary
  tasks:
    - kanban_card_for_high_signal_finding
verification:
  required:
    - every reported item includes a source URL or repo-relative source path
    - report records whether it acted autonomously or needs approval
    - state file prevents duplicate reporting on repeated runs
    - public publishing and product direction changes remain human-gated
  commands:
    - name: validate loop manifest
      run: uv run --with check-jsonschema --with pyyaml check-jsonschema --schemafile spec/loop.schema.json templates/research-watch/loop.yaml
      expected: exits 0 and prints ok -- validation done
    - name: dry-run runner against local example source
      run: python3 templates/research-watch/scripts/research_watch.py --sources templates/research-watch/examples/dry-run-sources.json --state /tmp/loopmaster-research-watch-state.json --output-dir /tmp/loopmaster-research-watch --max-items 5
      expected: exits 0 and prints RESEARCH_WATCH_REPORT and RESEARCH_WATCH_STATE paths
human_gates:
  required_for:
    - public publishing
    - product direction changes
    - credential changes
    - merging proposed resource entries
  approver: maintainer
failure_policy:
  retry: 2
  escalate_after: repeated_fetch_or_validation_failure
  rollback: leave the previous state file unchanged when report generation fails; delete only the failed run artifact after recording the error
maintenance:
  freshness_cadence: weekly
  owner: Loopmaster maintainers
  audit_log: loops/log/
backends:
  cron:
    schedule: "0 9 * * 1"
    install_notes:
      - Runs the checked-in Python helper from the target repository root.
      - Uses a persistent env file when optional credentials are enabled; no one-time shell exports are assumed to survive cron.
  github_actions:
    workflow: .github/workflows/research-watch.yml
    install_notes:
      - Workflow runs weekly or manually, writes a report, updates state, and opens a pull request with those changes.
  agent_kanban:
    task_template: Create a weekly research-watch task that runs the helper, reviews the report, and creates follow-up cards only for high-signal findings.
    install_notes:
      - Kanban installation assigns a recurring agent task; the agent still runs the same helper and verification commands.

Install guide (for your agent)

Raw for agents

Agent install guide — Research Watch Loop

Run commands from the repository root unless a step says otherwise. Replace paths only when your target repo intentionally uses different report/state locations.

This directory is a starter template, not an installed unattended loop. Copy it into the target repository, customize the source list, schedule, report/state paths, and any credential storage, then get human approval before enabling a scheduler, publishing findings, changing product direction, or using new credentials.

If you copied the template into another repository at templates/research-watch/, verify the copied files before installing a backend:

test -f templates/research-watch/loop.yaml
test -f templates/research-watch/AGENT-INSTALL.md
python3 templates/research-watch/scripts/research_watch.py --help >/tmp/loopmaster-research-watch-help.txt
uv run --with check-jsonschema --with pyyaml check-jsonschema \
  --schemafile spec/loop.schema.json \
  templates/research-watch/loop.yaml
rm -rf /tmp/loopmaster-research-watch /tmp/loopmaster-research-watch-state.json
python3 templates/research-watch/scripts/research_watch.py \
  --sources templates/research-watch/examples/dry-run-sources.json \
  --state /tmp/loopmaster-research-watch-state.json \
  --output-dir /tmp/loopmaster-research-watch \
  --max-items 5

Expected verification: the schema command prints ok -- validation done; the runner prints RESEARCH_WATCH_REPORT=..., RESEARCH_WATCH_STATE=..., NEW_ITEMS=1, and FETCH_ERRORS=0. If you copied the template under a different path, update every command and loop.yaml reference deliberately before running the checks.

0. Preflight

python3 --version
python3 templates/research-watch/scripts/research_watch.py --help
uv run --with check-jsonschema --with pyyaml check-jsonschema \
  --schemafile spec/loop.schema.json \
  templates/research-watch/loop.yaml

Expected output:

Python 3.x.x
usage: research_watch.py ...
ok -- validation done

Failure branches:

  • python3: command not found — install Python 3 or use a backend image that includes it.
  • No such file or directory — confirm you are at repo root and that templates/research-watch/ exists.
  • schema validation failure — fix loop.yaml before installing any backend.

1. Verify the concrete runner locally

rm -rf /tmp/loopmaster-research-watch /tmp/loopmaster-research-watch-state.json
python3 templates/research-watch/scripts/research_watch.py \
  --sources templates/research-watch/examples/dry-run-sources.json \
  --state /tmp/loopmaster-research-watch-state.json \
  --output-dir /tmp/loopmaster-research-watch \
  --max-items 5

Expected output shape:

RESEARCH_WATCH_REPORT=/tmp/loopmaster-research-watch/YYYY-MM-DD-research-watch.md
RESEARCH_WATCH_STATE=/tmp/loopmaster-research-watch-state.json
NEW_ITEMS=1
FETCH_ERRORS=0

Then verify duplicate suppression:

python3 templates/research-watch/scripts/research_watch.py \
  --sources templates/research-watch/examples/dry-run-sources.json \
  --state /tmp/loopmaster-research-watch-state.json \
  --output-dir /tmp/loopmaster-research-watch \
  --max-items 5

Expected output: NEW_ITEMS=0 and FETCH_ERRORS=0.

Failure branches:

  • FETCH_ERRORS is non-zero on the dry run — the local file source is unreadable; fix the path in examples/dry-run-sources.json.
  • report is missing source evidence — inspect the generated Markdown and fix the source config before scheduling.

2. Configure sources

Edit a project-local copy of templates/research-watch/examples/sources.json. Keep the top-level shape:

{
  "sources": [
    {"type": "rss", "name": "Example feed", "url": "https://example.com/feed.xml"},
    {"type": "url", "name": "Important page", "url": "https://example.com/important"},
    {"type": "github_repo", "name": "Example repo", "url": "https://github.com/example/repo"},
    {"type": "file", "name": "Local notes", "path": "docs/research/loop-landscape.md"}
  ]
}

Supported by the checked-in runner: file, url, rss, and github_repo.

The dry-run source file proves the starter template is runnable; it is not the live installed loop. A live loop is installed only after this source file is replaced with project-specific targets, report/state paths are chosen, credentials are persisted in the backend secret store if needed, and the chosen backend is enabled with maintainer approval.

No web-search credential exists by default. If no credential exists, do not add web_search to sources.json, do not add a web_search source to loop.yaml, and do not add a web-search credential scope or env var. This keeps README.md, loop.yaml, and runtime behavior consistent.

If you later add a real web-search provider, persist its secret in the backend secret store and update the manifest and runtime source file in the same PR. A one-time export WEB_SEARCH_API_KEY=... is not a valid cron install because cron does not inherit that interactive shell.

3. Backend A — cron

Cron runs on one machine and writes reports/state into that checkout. It does not open a PR by itself.

Create a persistent environment file, even if it currently contains no secrets:

mkdir -p .loopmaster/state .loopmaster/env docs/research
cat > .loopmaster/env/research-watch.env <<'EOF'
# Optional secrets go here as KEY=value lines.
# Do not commit this file.
RESEARCH_WATCH_SOURCES=templates/research-watch/examples/sources.json
RESEARCH_WATCH_STATE=.loopmaster/state/research-watch.json
RESEARCH_WATCH_OUTPUT_DIR=docs/research
EOF
chmod 600 .loopmaster/env/research-watch.env
printf '\n.loopmaster/env/*.env\n' >> .git/info/exclude

Install the cron entry:

REPO_ROOT=$(pwd)
CRON_LINE="0 9 * * 1 cd $REPO_ROOT && set -a && . ./.loopmaster/env/research-watch.env && set +a && /usr/bin/env python3 templates/research-watch/scripts/research_watch.py --sources \"$RESEARCH_WATCH_SOURCES\" --state \"$RESEARCH_WATCH_STATE\" --output-dir \"$RESEARCH_WATCH_OUTPUT_DIR\" >> .loopmaster/state/research-watch.cron.log 2>&1"
( crontab -l 2>/dev/null | grep -v 'templates/research-watch/scripts/research_watch.py' ; printf '%s\n' "$CRON_LINE" ) | crontab -
crontab -l | grep 'research_watch.py'

Expected output: one crontab line containing templates/research-watch/scripts/research_watch.py.

Test the exact command outside cron:

set -a && . ./.loopmaster/env/research-watch.env && set +a
python3 templates/research-watch/scripts/research_watch.py \
  --sources "$RESEARCH_WATCH_SOURCES" \
  --state "$RESEARCH_WATCH_STATE" \
  --output-dir "$RESEARCH_WATCH_OUTPUT_DIR"

Expected output: RESEARCH_WATCH_REPORT=docs/research/..., RESEARCH_WATCH_STATE=.loopmaster/state/research-watch.json, and counts.

Failure branches:

  • crontab: command not found — this backend is unavailable; use GitHub Actions or agent-kanban.
  • cron log shows missing env vars — check .loopmaster/env/research-watch.env permissions and the set -a && . ... segment.
  • secrets are needed — add KEY=value to .loopmaster/env/research-watch.env only; never commit it and never rely on a one-time interactive export.
  • FETCH_ERRORS is non-zero — inspect .loopmaster/state/research-watch.cron.log and the generated report; fix sources or network access before treating the run as verified.

Stop cron:

crontab -l | grep -v 'templates/research-watch/scripts/research_watch.py' | crontab -

4. Backend B — GitHub Actions

GitHub Actions runs weekly or manually, writes a report/state update, and opens a PR. It uses repository permissions, not local cron secrets.

Install the workflow:

mkdir -p .github/workflows .loopmaster/state docs/research
cp templates/research-watch/examples/github-actions/research-watch.yml .github/workflows/research-watch.yml
git add .github/workflows/research-watch.yml templates/research-watch/examples/sources.json
git diff --cached -- .github/workflows/research-watch.yml

Expected output: a workflow containing:

permissions:
  contents: write
  pull-requests: write

Commit and push the workflow in the normal PR flow. After merge, run it manually:

gh workflow run research-watch.yml
sleep 10
gh run list --workflow research-watch.yml --limit 1

Expected output shape:

STATUS  TITLE  WORKFLOW  BRANCH  EVENT  ID  ELAPSED  AGE

When the run succeeds, expected result is a new PR titled docs(research): add research watch report if the runner changed docs/research/*.md or .loopmaster/state/research-watch.json. If there are no changes because state already contains all items, peter-evans/create-pull-request completes without opening a duplicate PR.

Failure branches:

  • gh: command not found or unauthenticated — use the GitHub web UI to run the workflow, or authenticate gh.
  • workflow fails at checkout or permission step — confirm repo Actions permissions allow contents: write and pull-requests: write.
  • workflow fails in Run research watch — inspect the run log; fix examples/sources.json or network assumptions.
  • web-search source added without secret — remove the web_search source and matching manifest credential fields, or add the provider secret under GitHub repository Settings → Secrets and variables → Actions and reference it in the workflow env: block.

Disable GitHub Actions backend:

git rm .github/workflows/research-watch.yml
git commit -m "chore: disable research watch workflow"

5. Backend C — agent-kanban

Agent-kanban assigns a recurring task to an agent. The task must still run the concrete helper and record evidence; it is not a prose-only reminder.

For Hermes kanban, create a recurring scheduler job or board task whose prompt is self-contained. Example prompt:

Run the Loopmaster research-watch loop from repo root. Commands:
1. uv run --with check-jsonschema --with pyyaml check-jsonschema --schemafile spec/loop.schema.json templates/research-watch/loop.yaml
2. python3 templates/research-watch/scripts/research_watch.py --sources templates/research-watch/examples/sources.json --state .loopmaster/state/research-watch.json --output-dir docs/research
Then read the generated report path printed as RESEARCH_WATCH_REPORT. If NEW_ITEMS is greater than 0, summarize high-signal findings and create follow-up kanban cards only for evidence-backed actionable items. Do not publish public claims or change product direction without human approval. Complete with the report path, state path, counts, and any cards created.

Using Hermes native cron job scheduling from an agent session:

cronjob(action="create", name="Loopmaster research watch", schedule="0 9 * * 1", workdir="/absolute/path/to/repo", prompt="<the self-contained prompt above>")

Expected result: the scheduler returns a job id. On each run, completion evidence must include RESEARCH_WATCH_REPORT, RESEARCH_WATCH_STATE, NEW_ITEMS, and FETCH_ERRORS.

Failure branches:

  • no kanban or scheduler backend exists — use cron or GitHub Actions instead.
  • agent creates tasks without running the helper — reject the run; the backend is misinstalled because this template's verification requires the concrete helper output.
  • agent wants to publish findings publicly or change positioning — block for human approval as declared in human_gates.required_for.

6. Final install verification checklist

Before calling an install complete, record:

uv run --with check-jsonschema --with pyyaml check-jsonschema \
  --schemafile spec/loop.schema.json \
  templates/research-watch/loop.yaml
python3 templates/research-watch/scripts/research_watch.py \
  --sources templates/research-watch/examples/dry-run-sources.json \
  --state /tmp/loopmaster-research-watch-state.json \
  --output-dir /tmp/loopmaster-research-watch \
  --max-items 5

Expected success:

ok -- validation done
RESEARCH_WATCH_REPORT=/tmp/loopmaster-research-watch/YYYY-MM-DD-research-watch.md
RESEARCH_WATCH_STATE=/tmp/loopmaster-research-watch-state.json
NEW_ITEMS=0 or 1
FETCH_ERRORS=0

If any expected line is missing, the install is not verified.

How we know it works

Status
tested
Date
2026-07-07
Stale after
2026-08-06

dated checks passed

templates/research-watch/verification/dry-read-ambiguity-checklist.md

What it's allowed to do

6 allowed action(s), 4 forbidden action(s), 3 credential scope(s)

Allowed actions
read_web, read_repo, write_report, write_state, create_proposal_branch, create_task
Credential scopes
repo:contents:write, pull_request:write, kanban:create
Forbidden actions
publish_publicly_without_approval, spend_money, delete_user_data, mutate_source_systems