Files
grm/AGENTS.md
T
Emil SimeonovandDevin <158243242+devin-ai-integration[bot]@users.noreply.github.com> f8ae28601d fix: fetch rootless Docker scripts on Arch Linux
Arch Linux's docker package does not ship the rootless setup scripts
(dockerd-rootless-setuptool.sh, dockerd-rootless.sh) or rootlesskit,
unlike Debian's docker-ce-rootless-extras. The role now fetches them
from moby/moby contrib/ at a pinned ref and installs rootlesskit
explicitly.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-07-16 19:01:19 +02:00

30 KiB

AGENTS.md — Project Conventions for GRM

Virtual Environment

All Python tools, tests, and scripts run inside a standard .venv directory. Activate it before running any non-make command:

source activate.sh       # bash/zsh
source activate.fish     # fish
source activate.zsh      # zsh

If .venv doesn't exist, run make setup first. The make targets handle venv activation automatically — always prefer make <target> over raw commands.

Build & Test Commands

make setup          # Create venv, install deps, set up hooks, install CI tools
make install-tools  # Install actionlint, git-cliff, act_runner to ~/.local/bin
make lint-all       # ruff + pyright + bandit + ansible-lint + checkmake + actionlint
make pytest-cov     # Unit tests with 100% coverage enforcement
make test-unit      # Unit tests without coverage
make molecule       # All 6 scenarios on Ubuntu 22.04
make molecule-all   # All 6 scenarios on all 4 supported OSes
make test-all       # pytest-cov + molecule
make workflow-lint  # Static lint of .gitea/workflows/*.yml (actionlint)
make workflow-dryrun # Dry-run all workflows in Docker (act_runner exec --dryrun)
make workflow-check # workflow-lint + workflow-dryrun

make setup automatically installs all development tools:

  • Python deps via pip install -e .[dev] (includes devx from Gitea PyPI registry, configured by make configure-gitea-pypi)
  • Post-install setup via devx.tools.setup --skip-install (ansible-galaxy, pre-commit hooks, tea CLI login)
  • checkmake via devx.tools.install_checkmake (Makefile linter)
  • actionlint, git-cliff, act_runner, tea via devx.tools.install_tools (CI/CD tools to ~/.local/bin)

Workflow Verification (Before Push)

Workflow YAML files (.gitea/workflows/*.yml) are verified with two tools:

  1. actionlint — Static linter that catches syntax errors, invalid expressions, unknown keys, type mismatches, and shellcheck issues. Config: .gitea/actionlint.yaml (registers custom docker runner label). Installed automatically by make setup via devx.tools.install_tools.

  2. act_runner exec --dryrun — Gitea's own runner in dry-run mode. Validates job dependencies, step ordering, and Docker image selection without starting containers. Installed automatically by make setup.

Both run via make workflow-check and are part of make lint-all. The pre-commit hook runs actionlint automatically when workflow files change. The CI validate job runs make setup-image (which installs all tools) then make lint-all. CI also runs a best-effort make workflow-dryrun step (skipped if act_runner is not installed in the CI Docker image).

Architecture

  • Python CLI (src/grm/) — Click-based CLI that delegates to Ansible
  • Ansible Role (ansible/roles/gitea_runner/) — Idempotent role for rootless Docker runner setup with pasta networking (IPv6 support)
  • devx package (installed from git) — Reusable CI/CD tools: auto-merge, post-merge, release, publishing, molecule distribution, PR reviews, failure notifications
  • Versioning (cliff.toml) — git-cliff configuration for automated semver versioning from conventional commits

PR Workflow (Mandatory)

Every change to master goes through this workflow. No exceptions.

Branch Protection (Required Gitea Settings)

Branch protection and labels are automatically configured by devx.tools.configure_repo (run as python -m devx.tools.configure_repo), which runs as a step in the detect-and-configure job in the post-merge workflow on every push to master.

The following rules are enforced for master:

  • Require pull request: No direct pushes to master
  • Require approval review: At least 1 APPROVE review before merge
  • Require status checks: CI validate + molecule tests must pass
  • Block force pushes: No history rewriting on master

The auto-merge workflow enforces the APPROVE review check programmatically as a defense-in-depth measure, but branch protection is the primary gate.

1. Create Vikunja Task

Create a task in Vikunja project 6 via make create-task -- --title "Task title" --description "<h2>...</h2>" (requires VIKUNJA_TOKEN in .env). This prints the GRM-N identifier and next-step instructions.

IMPORTANT: The task title must NOT include the GRM-N: prefix. The make create-pr and check_auto_merge_ready commands automatically prepend GRM-N: to the Vikunja task title when forming the PR title. If the Vikunja task title already includes the prefix, the PR title will have a double prefix and auto-merge validation will fail.

2. Create Branch

git checkout master && git pull
git checkout -b GRM-N-short-description

3. Implement Changes

  • Write code following conventions below
  • Write/update tests (100% coverage required)
  • Update documentation (CHANGELOG, README, AGENTS.md as needed)

4. Commit (Conventional Commits)

Branch commits use conventional commit format (no GRM-N: prefix):

feat: add new feature
fix: resolve bug
docs: update README

5. Push and Create PR

  • Push: git push -u origin HEAD (pre-push hook validates Vikunja task existence via devx.tools.pre_push_check)
  • Create PR: make create-pr (creates a PR with title GRM-N: <vikunja task title>, auto-derived from the branch name and Vikunja task)
  • Or both in one step: make push-with-pr
  • PR body: summary of changes, Closes GRM-N
  • Add ready-to-merge label only after review is complete

6. Review the PR (Mandatory — Before Adding ready-to-merge Label)

Review checklist: Every PR is reviewed against 13 categories covering architecture, code quality, security, i18n, testing, performance, UX, documentation, workflow compliance, maintainability, resource management, backwards compatibility, and logging.

Automated review (CI validate job): Every PR triggers an automated review via python -m devx.ci.pr_review as a step in the validate job. This posts a review with COMMENT (no issues) or REQUEST_CHANGES (issues found) based on the [auto] items in the checklist:

  • Architecture compliance (no subprocess in CLI, no hardcoded URLs)
  • Best practices (no print(), no bare except, no TODO/FIXME, no functions > 50 lines)
  • Security (no hardcoded secrets, no shell=True, no eval/exec)
  • i18n (no raw strings in click.echo() without _() wrapper)
  • Resource management (no open() without with, no Popen() without cleanup)
  • Documentation (source changes must include doc updates)
  • Test coverage (source changes must include test updates)
  • Commit conventions (conventional commit format on PR commits)

The automated review posts inline comments on specific lines and includes a summary of the checklist categories. The agent must address all REQUEST_CHANGES issues before proceeding.

Manual review (agent): After the automated review passes, the agent must go through every category listed above and verify the [manual] items by reviewing the full diff (git diff master...HEAD).

Post review comments using devx.ci.pr_review (run as python -m devx.ci.pr_review):

CI_GITEA_TOKEN=<token> python -m devx.ci.pr_review <pr_number> <owner/repo> \
  --event REQUEST_CHANGES \
  --body "Review summary"

7. Address Review Comments

Fix each comment one by one, commit, and push. Re-review until satisfied.

8. Approve and Merge

Once all checklist items are verified and comments are addressed, post an approval review with --checklist-confirmed and --checklist-categories:

CI_GITEA_TOKEN=<token> python -m devx.ci.pr_review <pr_number> <owner/repo> \
  --event APPROVE --checklist-confirmed \
  --checklist-categories 1,2,3,4,5,6,7,8,9,10,11,12,13 \
  --body "All 13 checklist categories verified. Architecture: <summary>. Security: <summary>. Tests: <summary>. Docs: <summary>."

The --checklist-confirmed flag is required for APPROVE events — it attests that the reviewer has gone through every checklist category. The --checklist-categories flag is also required — it must list at least 8 of the 13 category numbers, ensuring the reviewer actually checked each category rather than rubber-stamping. The review body must be substantive (> 50 characters) — perfunctory approvals like "LGTM" are rejected.

Then add the ready-to-merge label. The auto-merge workflow will:

  1. Validate PR title format (GRM-N: <vikunja task title>) and match against Vikunja task title
  2. Check that at least one substantive APPROVE review exists (body > 20 chars or has inline comments)
  3. Wait for all CI checks to pass (including the validate job)
  4. Squash-merge with title: GRM-N: <conventional commit message>
  5. The post-merge workflow marks the Vikunja task as done
  6. The release-and-maintain job automatically versions, tags, and publishes (see below)

If the branch is behind master (another PR merged first), auto-merge automatically rebases the PR's head branch via the Gitea API. This triggers a new CI run. The next auto-merge attempt will merge successfully. No manual rebase needed. To rebase manually: make rebase (local) or make pr-rebase (server-side via API).

Important

: Never manually merge PRs via the API. Always use the auto-merge workflow by adding the ready-to-merge label. Manual merges bypass the GRM-N: <conventional> format enforcement, producing incorrectly named commits. The auto-merge script validates the PR title matches the Vikunja task ID and conventional commit format before merging.

CI Path Filtering

The CI workflow's validate job includes a pre-merge validation step that validates branch format, PR title, and Vikunja task match. This fails fast before expensive molecule tests run.

The validate job also includes a detect-changes step that checks whether any files under ansible/ or .ansible-lint have changed. If no Ansible files are changed, molecule tests are skipped — this prevents non-Ansible changes (e.g., Python scripts, workflow YAML, docs) from being blocked by molecule test infrastructure flakiness.

Dynamic Runner Discovery

Molecule tests are distributed across available Gitea Actions runners dynamically via devx.molecule.discover_runners. The validate job includes a discover-runners step (conditional on ansible-changed) that queries the Gitea API for runners at all levels (repo, org, instance) and generates a dynamic matrix. If the API can't see instance-level runners (no admin scope), it falls back to the MOLECULE_RUNNERS repo variable, then to a default of 3.

When adding/removing Gitea runners:

  1. Repo/org-level runners are auto-detected via the API
  2. For instance-level runners, update the MOLECULE_RUNNERS repo variable
  3. The workflow automatically scales the matrix to match available runners

Automated Release Pipeline

After a PR is merged to master, the post-merge workflow (.gitea/workflows/post-merge.yml) runs automatically. Consolidated into 2 jobs (from 7) to reduce runner overhead:

  1. detect-and-configure — Configures repo (branch protection, labels), detects release commit, validates commit message. Outputs is-release and is-automated for the next job.

  2. release-and-maintain — Runs all post-merge maintenance as conditional steps:

    • release (if not a release commit) — Runs devx.ci.release which checks for user-facing changes via classify_changes (skips if only workflow/infrastructure files changed), uses git-cliff for semver, updates __version__, updates CHANGELOG.md, runs lint+tests, commits with release: vX.Y.Z [skip ci], creates annotated tag, pushes to master.
    • publish (if release created a tag) — Builds and publishes the package to the Gitea PyPI registry. Checks out the release tag within the same job.
    • sync-wiki (if not automated) — Syncs documentation to the Gitea wiki.
    • vikunja (if not automated) — Marks the corresponding Vikunja task as done.
    • badges (always) — Generates and pushes quality badge SVGs to the badges branch. Fetches latest master first to pick up release commits.

Smart CI: User-Facing vs Workflow-Only Changes

Not all changes require the full CI pipeline or a new release. The project classifies changes into two categories using devx.ci.classify_changes:

Classification strategy (safe-by-default): Any file NOT in the explicit infrastructure allowlist is treated as user-facing. This prevents new file types from accidentally skipping releases. Classification is config-driven via [tool.devx.classify] in pyproject.toml.

Infrastructure paths (no release needed):

  • .gitea/** — Gitea Actions workflows
  • scripts/** — Dev tools and CI/CD automation (not part of installed package)
  • docs/** — Documentation
  • tests/** — Test files
  • AGENTS.md, README.md, CHANGELOG.md, TROUBLESHOOTING.md, CONTRIBUTING.md — Project docs
  • Makefile, cliff.toml, uv.lock — Build tooling
  • .pre-commit-config.yaml, .ansible-lint, .checkmake.ini — Lint config (ruff config is in pyproject.toml)
  • .env.example, .gitignore — Config
  • .devin/** — Agent/CI tooling config
  • hooks/** — Git hooks
  • activate.sh, activate.fish, activate.zsh — Generated venv scripts

User-facing paths (tool changes → release needed) — everything else:

  • src/grm/** — Python CLI source (except __init__.py)
  • ansible/** — Ansible role
  • pyproject.toml — Package metadata
  • Any new file type not in the allowlist

devx module structure (installed from git, not in this repo):

  • devx.ci.* — CI/CD automation (run by workflows): release, publish, auto_merge, classify_changes, detect_release_commit, push_badges, doc_coverage, sync_wiki, distribute_molecule, molecule_ci_guard, discover_runners, notify_failure, post_merge, pr_review, validate_commit_msg
  • devx.tools.* — Dev tools (run locally): check_test_speed, configure_repo, install_checkmake, install_tools, setup, generate_badges, create_task, create_pr, pr_status, pr_logs, pr_label, rebase, pr_rebase
  • devx.molecule.* — Molecule helpers: molecule_all, platforms, discover_runners, distribute_molecule, molecule_ci_guard
  • devx.gitea_cli — Tea CLI wrapper
  • devx.i18n — i18n translation system
  • devx.config — Shared configuration (DEVX_* env vars)
  • devx.api_clients — GiteaClient, VikunjaClient
  • devx.exceptions — APIError and other exceptions

CI behavior based on classification:

  • Molecule tests: Only run when ansible/ or .ansible-lint files change
  • Release dry-run: Only runs when user-facing files change
  • Quality job (lint, unit tests, coverage, doc-coverage): Always runs
  • Release workflow: Skips entirely when no user-facing files changed since last tag

AI agents must follow these rules:

  • When working on workflow/CI/docs-only changes, use ci: or docs: commit prefixes
  • Do NOT bump the version or create tags for infrastructure-only changes
  • The classify_changes module enforces this automatically — no manual intervention needed

Source Code Separation and devx Integration

The codebase enforces strict separation between the GRM tool and the devx package:

Directory Layout

Directory Purpose Release impact
src/grm/ User-facing GRM CLI tool Changes trigger release
devx package (installed from git) Reusable CI/CD and dev tools Not in this repo (no release impact)
ansible/ Ansible role for runner setup Changes trigger release

Import Rules

  1. src/grm/ NEVER imports from devx — the GRM tool is self-contained
  2. devx MAY import from grm — one-way dependency (devx uses the tool's API clients, config, i18n)
  3. Cross-module imports within devx are allowed (devx modules importing from other devx modules) and must be documented
  4. devx.gitea_cli is a shared wrapper around the tea CLI — devx modules import from it for Gitea API operations (issues, labels, PRs, releases, reviews)

tea CLI Integration

The tea Gitea CLI tool is used for Gitea API interactions in devx. It is installed by devx.tools.install_tools and configured by devx.tools.setup (login profile from .env CI_GITEA_TOKEN).

devx.gitea_cli — Python wrapper around tea CLI with JSON output parsing:

  • TeaCLI.create_issue() — Create issues with labels
  • TeaCLI.list_labels() / TeaCLI.create_label() / TeaCLI.add_label() — Label management
  • TeaCLI.create_pr() / TeaCLI.merge_pr() / TeaCLI.review_pr() — Pull request operations
  • TeaCLI.create_release() / TeaCLI.list_releases() — Release management
  • TeaCLI.list_branches() — Branch listing

Modules using tea (via devx.gitea_cli):

  • devx.ci.publish — Creates Gitea releases via tea releases create
  • devx.ci.notify_failure — Creates issues via tea issues create (falls back to GiteaClient if tea not installed)
  • devx.tools.configure_repo — Creates labels via tea labels create (falls back to GiteaClient if tea fails; branch protection still uses GiteaClient since tea only supports basic protect/unprotect)

Operations still using GiteaClient (not supported by tea):

  • PR reviews (devx.ci.pr_review) — tea v0.14.1 only supports interactive reviews
  • Wiki page management (devx.ci.sync_wiki)
  • Commit status checks (devx.ci.auto_merge)
  • Runner discovery (devx.molecule.discover_runners)
  • Branch protection with detailed config (devx.tools.configure_repo)
  • PR file/commit listing (devx.ci.pr_review)

PYTHONPATH Configuration

Since devx is installed as a package (via pip install from git), it is importable directly. Workflows only need PYTHONPATH=src when a devx module imports from grm:

PYTHONPATH When to use Example modules
src Module imports from grm devx.ci.auto_merge, devx.ci.pr_review, devx.ci.pr_review, devx.ci.sync_wiki, devx.ci.post_merge, devx.ci.classify_changes, devx.molecule.discover_runners, devx.ci.doc_coverage
(none) Module has no GRM imports devx.ci.detect_release_commit, devx.molecule.distribute_molecule, devx.molecule.molecule_ci_guard, devx.ci.push_badges, devx.ci.validate_commit_msg

In workflows, always use env: blocks (not inline PYTHONPATH=value):

- name: Run module
  env:
    PYTHONPATH: src
  run: python -m devx.ci.example

Locally, devx is installed as a package, so only PYTHONPATH=src is needed if importing from grm.

Shared Constants

devx.molecule.platforms is the single source of truth for the molecule platform matrix. Both devx.molecule.distribute_molecule (CI) and devx.molecule.molecule_all (dev tool) import PLATFORMS from it — this avoids dev tools importing directly from CI modules.

  1. Publish step (in the release-and-maintain job, runs after the release step creates a tag):
    • Runs after the release step creates a tag
    • Gets the tag from the release step's output
    • Builds the Python package
    • Publishes to the Gitea PyPI registry
    • Creates a Gitea release with git-cliff-generated release notes
    • On failure, creates a Gitea issue via devx.ci.notify_failure

git-cliff Commit Preprocessing

Merge commits on master have the format GRM-N: <conventional commit>. The GRM-N: prefix is not a valid conventional commit prefix, so cliff.toml includes a commit_preprocessors entry that strips it before parsing. This ensures all merged work appears in the changelog.

Version Bumping Rules (git-cliff)

Commit type Version bump
feat: minor (0.X.0)
fix: patch (0.0.X)
feat!: or BREAKING CHANGE minor (pre-1.0: major would be 1.0.0)
chore:, ci:, docs: no bump (excluded by cliff.toml)

The version source is __version__ in src/grm/__init__.py, read by setuptools via dynamic = ["version"] in pyproject.toml. The release script only updates __init__.py — no need to touch pyproject.toml. grm --version reports this version.

Title Format Summary

What Format Example
Branch name GRM-N-short-description GRM-33-add-pr-review-step
Branch commits <conventional commit> feat: add review script
PR title GRM-N: <vikunja task title> GRM-33: Add mandatory PR review step
Merge commit GRM-N: <conventional commit> GRM-33: feat: add review script

Configuration

The devx package is configured via DEVX_* environment variables:

  • DEVX_TASK_PREFIX=GRM — Prefix for Vikunja task identifiers
  • DEVX_VIKUNJA_PROJECT_ID=6 — Vikunja project ID for task tracking
  • DEVX_VERSION_FILE=src/grm/__init__.py — Path to the version source file

Change classification is config-driven via [tool.devx.classify] in pyproject.toml, which defines the infrastructure and user-facing path patterns.

Key Conventions

  • Python 3.12+ required (ruff/pyright target py312)
  • 100% test coverage required (--cov-fail-under=100)
  • Conventional commits on feature branches (no GRM-N: prefix)
  • Branch names must include GRM-N task ID
  • Line length: 120 chars
  • Secrets are passed via temp JSON files, never on the command line (CWE-214)
  • CI triggers only on opened and synchronize PR events (not labeled)

Testing Conventions

  • Always run make pytest-cov before pushing — CI enforces 100% coverage and will fail the PR if any lines are uncovered. The pre-push hook only validates Vikunja task existence, not tests.
  • Never use is True/is False identity checks on API response values — many APIs return boolean values as strings ("true"/ "false"). Use string comparison or truthy/falsy helpers instead.
  • Always mock time.sleep and time.monotonic in unit tests — real sleep calls make tests slow and exceed test speed limits. Use @patch("time.sleep") and @patch("time.monotonic") decorators.
  • Extract complex inline shell from workflows to tested Python tools — SSH loops, curl polling, docker exec chains, and multi-line if/then/else shell blocks should be Python scripts in scripts/ with unit tests. Simple variable checks and venv activation are fine as inline shell.

Container-Level Fix Verification (Mandatory)

Rule: Before pushing any fix that modifies container state (CA certs, config files, installed packages, daemon restarts), reproduce the exact sequence locally with the actual Docker image. Do not push to CI as the first test.

This is a hard rule, not a suggestion. CI cycles take 20+ minutes and ephemeral staging VMs are destroyed after each run, making interactive debugging impossible. A local reproduction takes 30 seconds and catches silent failures immediately.

Procedure:

  1. docker pull <actual_image>
  2. docker run -d --name <test> ... and wait for it to start
  3. Run the exact commands from the Ansible task or script
  4. Verify the state change took effect
  5. Clean up: docker rm -f <test>

Verified State Modification (Mandatory)

Ansible tasks that modify container state with changed_when: false MUST include a post-task verification step that confirms the state change took effect. changed_when: false suppresses both change detection AND failure visibility — a task can silently do nothing and report ok.

Ansible Role Structure

main.yml → systemd_check → user_setup → rootless_docker → install_runner → prune → integration_test
  • install_runner.yml handles: download, config, validate, register, service
  • main.yml handles: prune, integration_test (NOT install_runner — avoids duplicates)
  • systemctl --user tasks must be guarded by docker_rootless_setup
  • Template creation tasks are NOT guarded by docker_rootless_setup (they only create files)
  • On Arch Linux, rootless_docker.yml fetches the rootless setup scripts (dockerd-rootless-setuptool.sh, dockerd-rootless.sh) from moby/moby contrib/ at a pinned ref (gitea_runner_rootless_scripts_ref) into /usr/bin and installs rootlesskit — Arch's docker package ships neither. These fetch tasks run regardless of docker_rootless_setup so CI exercises them on the archlinux platform. See ADR-011 in the decision log.

Molecule Scenarios

7 scenarios: default, multi-instance, lifecycle, template-content, deregister, update, remove 4 platforms: ubuntu-2204, ubuntu-2404, debian-12, archlinux Platform list is defined in devx.molecule.platforms (single source of truth)

Note: make molecule and make molecule-all run 6 scenarios (excluding remove, which destroys the test container). CI discovers all 7 scenarios via devx.molecule.distribute_molecule.

Known Issues

  • ansible-lint may warn about command-instead-of-module for systemctl --user calls — this is expected (systemd module doesn't support user services) and skipped in .ansible-lint
  • Molecule Docker driver may print "Event loop is closed" warnings on interrupt — harmless

Documentation-as-Code

All documentation lives in /docs/ and is synced to the Gitea wiki automatically.

Structure

docs/
├── index.md              # Wiki homepage
├── mapping.json          # File-to-wiki-page title mapping
├── user/                 # User documentation
│   ├── getting-started.md
│   ├── installation.md
│   ├── cli-commands.md
│   ├── troubleshooting.md
│   └── faq.md
└── tech/                 # Technical documentation
    ├── architecture.md
    ├── development-setup.md
    ├── ci-cd-workflow.md
    ├── testing-strategy.md
    ├── decision-log.md
    └── contributing.md

Wiki Sync

  • On merge to master: sync-wiki.yml workflow runs devx.ci.sync_wiki which pushes all /docs/ content to the Gitea wiki via API
  • On release tag: Same sync runs, plus the wiki is tagged with the release version
  • mapping.json maps each file path to a wiki page title (e.g., user/getting-started.mdGetting-Started)
  • README.md is a lean entry point with links to the wiki — no detailed content

Documentation Coverage

  • devx.ci.doc_coverage checks that all CLI commands, Python modules, and CI scripts are documented
  • Runs as a CI step in the validate job with --fail-on-missing (blocks CI if docs are missing)
  • Enforced: 100% coverage for public CLI commands and major architectural components

Updating Documentation

  1. Edit files in /docs/
  2. If adding a new page, add it to docs/mapping.json
  3. Commit and create a PR (standard PR workflow)
  4. On merge, wiki is automatically synced

Subagent Delegation Policy

Custom subagent profiles are defined in .devin/agents/ (project-specific) and ~/.config/devin/agents/ (global, shared across repos). The agent MUST automatically delegate to the appropriate subagent based on the task — the user should not need to specify which profile to use.

Available Profiles

Global (shared across all projects):

Profile Location Purpose
pr-reviewer ~/.config/devin/agents/ 13-category PR checklist + quality gates
release-check ~/.config/devin/agents/ Pre-merge readiness validation

grm-specific (in .devin/agents/):

Profile Purpose
ci-investigator Investigate CI failures (validate, molecule-tests, release-and-maintain)
molecule-runner Run 7 molecule scenarios across 4 platforms, report pass/fail
dep-upgrader Python + Ansible dependency upgrades with molecule verification
doc-sync-specialist Doc coverage, doc linting, wiki sync for grm docs
workflow-validator actionlint + act_runner dry-run for grm workflows

When to Delegate Automatically

Trigger Profile Mode
CI run failure (validate, molecule-tests, release-and-maintain) ci-investigator Background
PR ready for review pr-reviewer Foreground
Molecule tests need to run molecule-runner Background
Dependency upgrade requested dep-upgrader Background
Doc coverage failure or wiki sync issue doc-sync-specialist Background
Workflow YAML modified or validation needed workflow-validator Background
Branch ready for merge release-check Foreground

Delegation Rules

  1. Auto-select the profile. Do not ask the user which profile to use.
  2. Background by default, foreground when blocking.
  3. Provide full context in the prompt — subagents don't inherit conversation history.
  4. One subagent per concern. Chain: investigate → fix in main session → review.
  5. Don't delegate minor work (<30s, <50 lines of context).
  6. Compact after subagent returns.
  7. Never skip delegation to save time — it keeps main context small.

Feedback Issue Handling

Subagents create Gitea issues in the current repo when they encounter tool, workflow, or process issues that warrant follow-up. These issues use the feedback label plus a category label (tooling, ci-improvement, doc-improvement, workflow-improvement).

Standard labels are created automatically by configure_repo (runs as a step in detect-and-configure in post-merge on every master push). If a label does not exist yet, the subagent's issue creation will still succeed — labels can be added afterwards.

When a Subagent Reports a Feedback Issue URL

  1. Acknowledge it in your response to the user — mention the issue URL
  2. Do NOT close or modify the issue — it is for follow-up work
  3. Do NOT create a PR to address it unless the user explicitly asks
  4. If the user asks to address feedback, spawn a subagent to investigate the issue and implement a fix

Creating Feedback Issues Manually

As the parent agent, you can also create feedback issues directly using the Gitea MCP (issue_write with create_issue method). Follow the same format as subagents:

  • Title: [feedback] <category>: <short description>
  • Labels: feedback + category label
  • Body: include context, tool/workflow, issue, reproduction, affected files, suggested investigation, and "Reported by: parent agent"

Always deduplicate first via list_issues with labels: "feedback".