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 bymake 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:
-
actionlint — Static linter that catches syntax errors, invalid expressions, unknown keys, type mismatches, and shellcheck issues. Config:
.gitea/actionlint.yaml(registers customdockerrunner label). Installed automatically bymake setupviadevx.tools.install_tools. -
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
APPROVEreview 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 viadevx.tools.pre_push_check) - Create PR:
make create-pr(creates a PR with titleGRM-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-mergelabel 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 bareexcept, noTODO/FIXME, no functions > 50 lines) - Security (no hardcoded secrets, no
shell=True, noeval/exec) - i18n (no raw strings in
click.echo()without_()wrapper) - Resource management (no
open()withoutwith, noPopen()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:
- Validate PR title format (
GRM-N: <vikunja task title>) and match against Vikunja task title - Check that at least one substantive APPROVE review exists (body > 20 chars or has inline comments)
- Wait for all CI checks to pass (including the
validatejob) - Squash-merge with title:
GRM-N: <conventional commit message> - The post-merge workflow marks the Vikunja task as done
- 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-mergelabel. Manual merges bypass theGRM-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:
- Repo/org-level runners are auto-detected via the API
- For instance-level runners, update the
MOLECULE_RUNNERSrepo variable - 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:
-
detect-and-configure — Configures repo (branch protection, labels), detects release commit, validates commit message. Outputs
is-releaseandis-automatedfor the next job. -
release-and-maintain — Runs all post-merge maintenance as conditional steps:
- release (if not a release commit) — Runs
devx.ci.releasewhich checks for user-facing changes viaclassify_changes(skips if only workflow/infrastructure files changed), uses git-cliff for semver, updates__version__, updatesCHANGELOG.md, runs lint+tests, commits withrelease: 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
badgesbranch. Fetches latest master first to pick up release commits.
- release (if not a release commit) — Runs
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 workflowsscripts/**— Dev tools and CI/CD automation (not part of installed package)docs/**— Documentationtests/**— Test filesAGENTS.md,README.md,CHANGELOG.md,TROUBLESHOOTING.md,CONTRIBUTING.md— Project docsMakefile,cliff.toml,uv.lock— Build tooling.pre-commit-config.yaml,.ansible-lint,.checkmake.ini— Lint config (ruff config is inpyproject.toml).env.example,.gitignore— Config.devin/**— Agent/CI tooling confighooks/**— Git hooksactivate.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 rolepyproject.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, discover_runners, notify_failure, post_merge, pr_review, validate_commit_msgdevx.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_rebasedevx.molecule.*— Molecule helpers: molecule_all, platforms, discover_runners, distribute_moleculedevx.gitea_cli— Tea CLI wrapperdevx.i18n— i18n translation systemdevx.config— Shared configuration (DEVX_* env vars)devx.api_clients— GiteaClient, VikunjaClientdevx.exceptions— APIError and other exceptions
CI behavior based on classification:
- Molecule tests: Only run when
ansible/or.ansible-lintfiles 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:ordocs:commit prefixes - Do NOT bump the version or create tags for infrastructure-only changes
- The
classify_changesmodule 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
src/grm/NEVER imports from devx — the GRM tool is self-contained- devx MAY import from
grm— one-way dependency (devx uses the tool's API clients, config, i18n) - Cross-module imports within devx are allowed (devx modules importing from other devx modules) and must be documented
devx.gitea_cliis a shared wrapper around theteaCLI — 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 labelsTeaCLI.list_labels()/TeaCLI.create_label()/TeaCLI.add_label()— Label managementTeaCLI.create_pr()/TeaCLI.merge_pr()/TeaCLI.review_pr()— Pull request operationsTeaCLI.create_release()/TeaCLI.list_releases()— Release managementTeaCLI.list_branches()— Branch listing
Modules using tea (via devx.gitea_cli):
devx.ci.publish— Creates Gitea releases viatea releases createdevx.ci.notify_failure— Creates issues viatea issues create(falls back toGiteaClientif tea not installed)devx.tools.configure_repo— Creates labels viatea labels create(falls back toGiteaClientif tea fails; branch protection still usesGiteaClientsince 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.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.
- Publish step (in the
release-and-maintainjob, 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 identifiersDEVX_VIKUNJA_PROJECT_ID=6— Vikunja project ID for task trackingDEVX_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-Ntask ID - Line length: 120 chars
- Secrets are passed via temp JSON files, never on the command line (CWE-214)
- CI triggers only on
openedandsynchronizePR events (notlabeled)
Testing Conventions
- Always run
make pytest-covbefore 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 Falseidentity 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.sleepandtime.monotonicin 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:
docker pull <actual_image>docker run -d --name <test> ...and wait for it to start- Run the exact commands from the Ansible task or script
- Verify the state change took effect
- 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.ymlhandles: download, config, validate, register, servicemain.ymlhandles: prune, integration_test (NOT install_runner — avoids duplicates)systemctl --usertasks must be guarded bydocker_rootless_setup- Template creation tasks are NOT guarded by
docker_rootless_setup(they only create files) - On Arch Linux,
rootless_docker.ymlfetches the rootless setup scripts (dockerd-rootless-setuptool.sh,dockerd-rootless.sh) frommoby/mobycontrib/at a pinned ref (gitea_runner_rootless_scripts_ref) into/usr/binand installsrootlesskit— Arch'sdockerpackage ships neither. These fetch tasks run regardless ofdocker_rootless_setupso 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-lintmay warn aboutcommand-instead-of-moduleforsystemctl --usercalls — 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.ymlworkflow runsdevx.ci.sync_wikiwhich 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.jsonmaps each file path to a wiki page title (e.g.,user/getting-started.md→Getting-Started)- README.md is a lean entry point with links to the wiki — no detailed content
Documentation Coverage
devx.ci.doc_coveragechecks 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
- Edit files in
/docs/ - If adding a new page, add it to
docs/mapping.json - Commit and create a PR (standard PR workflow)
- 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
- Auto-select the profile. Do not ask the user which profile to use.
- Background by default, foreground when blocking.
- Provide full context in the prompt — subagents don't inherit conversation history.
- One subagent per concern. Chain: investigate → fix in main session → review.
- Don't delegate minor work (<30s, <50 lines of context).
- Compact after subagent returns.
- 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
- Acknowledge it in your response to the user — mention the issue URL
- Do NOT close or modify the issue — it is for follow-up work
- Do NOT create a PR to address it unless the user explicitly asks
- 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".