Loop template

not tested yetcatalog data (JSON)

Resource Discovery Loop

Sweep committed sources and rotating queries each week, score candidates against the resource selection notes, and propose ready-to-merge entries for the curated library without publishing them.

Human guide

Raw README.md

Loopmaster maintenance loops

This directory contains Loopmaster's own self-maintenance loop manifests. Each *.loop.yaml file declares a recurring maintenance loop using the draft v0.1 manifest format in spec/loop-spec-v0.1.md.

Current loops:

  • link-audit.loop.yaml — checks documentation and resource URLs, writes reports to loops/log/, and escalates non-trivial source replacement.
  • landscape-watch.loop.yaml — Loopmaster's own instance of the research-watch template. Polls the AI loop / agent-tooling landscape weekly and writes cited findings reports to docs/research/. Findings publish autonomously; public positioning or recommendation changes require maintainer approval.
  • resource-discovery.loop.yaml — sweeps committed sources plus rotating queries weekly, dedupes against the resource library, writes candidate reports to loops/log/, and records review-only proposal YAML under loops/proposals/. Publishing entries to resources/resources.yaml is gated.
  • loopmaster-content-freshness.loop.yaml — Loopmaster's own instance of the content-freshness template. Audits public docs, learning content, template guides, maintenance logs, and resource metadata monthly, writes dated reports to loops/log/, and keeps rewrites/deletions/product-positioning changes human-gated.

Scheduled runs use GitHub Actions where possible so audit evidence, safe automated changes, and failures remain visible in the repository history.

The job description (loop.yaml)

View raw loop.yaml

loops/resource-discovery.loop.yaml

spec_version: "0.1"
id: resource-discovery
name: Resource Discovery Loop
purpose: Sweep committed sources and rotating queries each week, score candidates against the resource rubric, and propose ready-to-merge entries for the curated library without publishing them.
audience:
  - human
  - agent
  - operator
trigger:
  type: schedule
  default: weekly
  schedule: "30 9 * * 1"
inputs:
  sources:
    - type: file
      name: Committed sweep list (SOURCES.md)
      path: resources/SOURCES.md
    - type: file
      name: Resource rubric (RUBRIC.md)
      path: resources/RUBRIC.md
    - type: file
      name: Current library (resources.yaml) for dedupe
      path: resources/resources.yaml
    - type: rss
      name: Practitioner and lab engineering feeds
      url: loops/resource-discovery.sources.json
    - type: url
      name: Stable source URLs
      url: loops/resource-discovery.sources.json
    - type: github_repo
      name: Agent framework and eval repositories
      url: loops/resource-discovery.sources.json
  files:
    - resources/SOURCES.md
    - resources/RUBRIC.md
    - resources/resources.yaml
    - resources/schema.json
    - loops/resource-discovery.sources.json
    - tools/resource_discovery.py
capabilities:
  - polling
  - source_summarization
  - dedupe
  - rubric_scoring
  - 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
    - merge_resource_entries_without_approval
    - spend_money
    - delete_user_data
    - mutate_source_systems
  credential_scopes:
    - repo:contents:write
    - pull_request:write
    - kanban:create
state:
  paths:
    - .loopmaster/state/resource-discovery.json
  retention: 180 days
outputs:
  artifacts:
    - loops/log/YYYY-MM-DD-resource-discovery.md
    - loops/proposals/YYYY-MM-DD-resource-discovery.proposal.yaml
  notifications:
    - run_summary
  tasks:
    - kanban_card_for_proposed_resource_entries
verification:
  required:
    - every proposed candidate includes a source URL and per-dimension rubric scores
    - every run records at least one rejected candidate with scores when candidates were considered
    - candidates are deduped against resources/resources.yaml and the loop state file
    - proposing is autonomous; merging entries to resources.yaml remains human-gated
    - 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 loops/resource-discovery.loop.yaml
      expected: exits 0 and prints ok -- validation done
    - name: dry-run runner against local source list
      run: python3 tools/resource_discovery.py --sources loops/resource-discovery.sources.json --state /tmp/loopmaster-resource-discovery-state.json --output-dir /tmp/loopmaster-resource-discovery --resources resources/resources.yaml --max-items 5
      expected: exits 0 and prints RESOURCE_DISCOVERY_REPORT and RESOURCE_DISCOVERY_STATE paths
    - name: verify duplicate suppression on repeat run
      run: python3 tools/resource_discovery.py --sources loops/resource-discovery.sources.json --state /tmp/loopmaster-resource-discovery-state.json --output-dir /tmp/loopmaster-resource-discovery --resources resources/resources.yaml --max-items 5
      expected: exits 0 and NEW_CANDIDATES=0 on the second consecutive run
human_gates:
  required_for:
    - merging proposed resource entries into resources/resources.yaml
    - public publishing
    - product direction changes
    - credential changes
  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: "30 9 * * 1"
    install_notes:
      - Runs the checked-in Python helper from the target repository root and writes the candidate report plus state.
      - Uses a persistent env file when optional credentials are enabled; no one-time shell exports are assumed to survive cron.
      - Cron writes the candidate list only; rubric scoring and proposal PRs are performed by the agent-kanban backend or a human.
  github_actions:
    workflow: .github/workflows/resource-discovery.yml
    install_notes:
      - Workflow runs weekly or manually, writes a candidate report, updates state, and opens a pull request with the report and any proposal YAML.
      - Proposal PRs are review-only; the workflow never merges entries into resources/resources.yaml.
  agent_kanban:
    task_template: Create a weekly resource-discovery task that runs the helper, scores candidates against RUBRIC.md, writes the five-part report with proposal YAML, and opens a review card for any candidate scoring >=7. Do not merge entries without human approval.
    install_notes:
      - Kanban installation assigns a recurring agent task; the agent still runs the same helper and verification commands before scoring.
      - The agent records rejected candidates in the report to prove the rubric has teeth.

Install guide (for your agent)

Raw for agents

Installing Loopmaster maintenance loops

The link audit loop is installed through .github/workflows/link-audit.yml.

What the schedule does:

  1. Runs uv run --with pyyaml python tools/link_audit.py --write from the repository root every Monday at 10:23 UTC.
  2. Writes a five-part report to loops/log/YYYY-MM-DD-link-audit.md.
  3. Commits the report plus safe autonomous updates, limited to same-host/trivial redirect rewrites and resources/resources.yaml status or last_verified stamps.
  4. Fails the workflow when a dead link is detected so maintainers see the escalation.

Manual run:

uv run --with pyyaml python tools/link_audit.py --write

Dead-link probe:

uv run --with pyyaml python tools/link_audit.py --no-report --extra-url https://example.com/__loopmaster_dead_link_probe__

Human gate: do not replace a source with a different source autonomously. Record the dead or non-trivial replacement in the report and let a maintainer approve the change.

Landscape watch

The landscape watch loop is an instance of the research-watch template (templates/research-watch/loop.yaml). It is installed through .github/workflows/landscape-watch.yml and reuses templates/research-watch/scripts/research_watch.py.

What the schedule does:

  1. Runs python3 templates/research-watch/scripts/research_watch.py --report-basename landscape-watch --sources loops/sources/landscape-watch.json --state .loopmaster/state/landscape-watch.json --output-dir docs/research from the repository root every Monday at 09:30 UTC.
  2. Polls public RSS, URL, and GitHub-repo sources listed in loops/sources/landscape-watch.json (OpenAI, Hugging Face, Karpathy, Anthropic, LangChain, CrewAI, OpenAI Agents SDK, AutoGen, LangGraph, A2A, MCP, and the matching repos).
  3. Dedupes against .loopmaster/state/landscape-watch.json so repeated runs do not re-report seen items.
  4. Writes a dated, cited, five-part report to docs/research/YYYY-MM-DD-landscape-watch.md.
  5. Opens a pull request with the report and state changes. No web-search credential is required.

Manual run:

python3 templates/research-watch/scripts/research_watch.py \
  --report-basename landscape-watch \
  --sources loops/sources/landscape-watch.json \
  --state .loopmaster/state/landscape-watch.json \
  --output-dir docs/research

Dedupe check (second run reports NEW_ITEMS=0):

python3 templates/research-watch/scripts/research_watch.py \
  --report-basename landscape-watch \
  --sources loops/sources/landscape-watch.json \
  --state .loopmaster/state/landscape-watch.json \
  --output-dir docs/research

Human gate: findings publish autonomously; changes to public recommendations, positioning, or product direction require maintainer approval. Do not merge the report PR if it changes Loopmaster's public positioning.

Resource discovery

The resource-discovery loop (loops/resource-discovery.loop.yaml) is an instance of the research-watch pattern tuned for resource-library proposals. Its concrete runner is tools/resource_discovery.py, and the scheduled backend is .github/workflows/resource-discovery.yml.

What the schedule does:

  1. Runs uv run --with pyyaml python tools/resource_discovery.py --sources loops/resource-discovery.sources.json --state .loopmaster/state/resource-discovery.json --output-dir loops/log --resources resources/resources.yaml --max-items 5 every Monday at 09:30 UTC.
  2. Dedupes against both resources/resources.yaml and .loopmaster/state/resource-discovery.json.
  3. Writes a five-part report to loops/log/YYYY-MM-DD-resource-discovery.md and updates the state ledger.
  4. Opens a review-only pull request with report/state changes; scored proposal YAML can be added by an agent or maintainer after applying resources/RUBRIC.md.

Manual run:

uv run --with pyyaml python tools/resource_discovery.py \
  --sources loops/resource-discovery.sources.json \
  --state .loopmaster/state/resource-discovery.json \
  --output-dir loops/log \
  --resources resources/resources.yaml \
  --max-items 5 \
  --exploratory "rotating queries this run: ..."

Expected output shape:

RESOURCE_DISCOVERY_REPORT=loops/log/YYYY-MM-DD-resource-discovery.md
RESOURCE_DISCOVERY_STATE=.loopmaster/state/resource-discovery.json
NEW_CANDIDATES=<n>
FETCH_ERRORS=<n>

Dedupe verification: run the same command a second time against the same state and expect NEW_CANDIDATES=0.

Scoring and proposing: after a run with NEW_CANDIDATES > 0, score each candidate against resources/RUBRIC.md (five dimensions, 0–2 each). Write ready-to-merge YAML for candidates scoring ≥7 with no dimension at 0 into loops/proposals/YYYY-MM-DD-resource-discovery.proposal.yaml, and record at least one rejected candidate with scores to prove the rubric has teeth.

Human gate: do not merge proposed entries into resources/resources.yaml without maintainer approval. Proposing is autonomous; publishing is gated.

Content freshness

The content-freshness loop (loops/loopmaster-content-freshness.loop.yaml) is Loopmaster's own instance of the content-freshness template. Its concrete runner is templates/content-freshness/scripts/content_freshness.py, configured by loops/content-freshness.targets.json, and the scheduled backend is .github/workflows/content-freshness.yml.

What the schedule does:

  1. Runs python3 templates/content-freshness/scripts/content_freshness.py --targets loops/content-freshness.targets.json --state .loopmaster/state/content-freshness.json --output-dir loops/log --max-link-checks 40 monthly at 10:00 UTC on the first day of the month.
  2. Scans public docs, authored site content, template guides, maintenance-loop docs, and resource metadata for stale files, stale markers, and checked-link failures.
  3. Writes a dated report to loops/log/YYYY-MM-DD-content-freshness.md and updates .loopmaster/state/content-freshness.json.
  4. Opens a review-only pull request with the report and state changes.

Manual run:

python3 templates/content-freshness/scripts/content_freshness.py \
  --targets loops/content-freshness.targets.json \
  --state .loopmaster/state/content-freshness.json \
  --output-dir loops/log \
  --max-link-checks 40

Expected output shape:

CONTENT_FRESHNESS_REPORT=loops/log/YYYY-MM-DD-content-freshness.md
CONTENT_FRESHNESS_STATE=.loopmaster/state/content-freshness.json
FINDINGS=<n>
AUDIT_ERRORS=<n>
LINKS_CHECKED=<n>

Human gate: the loop may report stale evidence, broken links, and source-backed recommendations. Do not rewrite public copy, delete content, or change product positioning without maintainer approval.

How we know it works

Status
not tested yet
Date
not tested yet
Stale after

no dated passing checks found under verification/

    What it's allowed to do

    6 allowed action(s), 5 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, merge_resource_entries_without_approval, spend_money, delete_user_data, mutate_source_systems