49 KiB
LLM-Operated Gitea Workflow Runbooks
Purpose
Runbooks for the common Gitea workflows an LLM performs through the gitea-mcp
package of the MCP Control Plane: creating issues, implementing them, opening
and reviewing pull requests, merging, and closing out — safely and
reproducibly.
For the project-agnostic version of these operating rules (issue-first, isolated worktrees, no self-review/merge, profile safety, cleanup, fail-closed) that can be copied into any repository, see the reusable skill
skills/llm-project-workflow/SKILL.mdand itstemplates/. This runbook is the Gitea-specific application of it.
These runbooks are operational guidance only. They add no tooling; the behavior they rely on already exists (canonical runtime profiles, the interactive setup menu, identity/eligibility checks, gated review/merge, and audit logging). See Related documents.
New session? Call the guide tools first (#128 / #129). Before using any other Gitea MCP tool in a fresh session, call
mcp_get_control_plane_guide(read-only): it reports the active profile, authenticated identity, allowed/forbidden operations, profile-aware do/don't guidance, and the non-negotiable rules (hard stops, fail-closed behavior, head-SHA pinning, merge confirmation, redaction, author/reviewer separation, profile switching). Also callgitea_get_runtime_contextandmcp_list_project_skillsto discover the available project workflows andmcp_get_skill_guide(<name>)for step-by-step instructions. This replaces long pasted operator prompts for the standard rules; operator prompts still control task-specific scope. See issue #129 for the skill registry design.
Jenkins and GlitchTip workflows use separate MCP servers, not this Gitea MCP
runtime. Register them as jenkins-mcp and glitchtip-mcp, reconnect or
reload the client, and verify visible tools before claiming either integration
is usable. See mcp-client-registration.md.
For cross-project use, copy the portable workflow skill at
../skills/llm-project-workflow/SKILL.md.
It extracts the issue-first, isolated-worktree, no-self-review, profile-safety,
merge-cleanup, fail-closed, and recovery rules into a reusable package that can
be adapted to other repositories.
Principle: the profile is the role, not the LLM
The LLM is not the role.
The MCP execution profile used for the task is the role.
An LLM session is never permanently an "author," "reviewer," or "merger." Any
session may perform any of these roles — but only while operating through a
task-appropriate profile whose authenticated Gitea identity and allowed
operations fit the task. A task selects a profile; a profile is not assigned to
a model. See gitea-execution-profiles.md.
Example role-scoped instructions:
Use an author profile to implement issue #N and open a PR.
Use any eligible reviewer profile to review PR #N.
Use any eligible merger profile to merge PR #N if checks pass.
Attribution: LLM-Agent-SHA (metadata only)
Sessions may attribute their work with an opaque LLM-Agent-SHA
(llm-<12 lowercase hex>, e.g. llm-8f3a9c2d6b41) in PR-body and
review-handoff metadata blocks — see
llm-agent-sha.md for the full convention. It is
attribution only: eligibility is decided solely by the authenticated
Gitea user and the profile's allowed operations. Two sessions with different
SHAs under the same Gitea user are the same actor — a different SHA never
permits self-review or self-merge. Keep the SHA out of branch and worktree
names.
Prerequisites: canonical config + thin launchers
Runtime profiles live in one canonical JSON file, referenced by every LLM launcher. No client config contains raw credentials.
Canonical config file
Selected by two environment variables:
GITEA_MCP_CONFIG— path to the canonical file (e.g.~/.config/gitea-tools/profiles.json).GITEA_MCP_PROFILE— the named profile to activate.
Shape (see ../gitea-mcp.example.json):
{
"version": 1,
"profiles": {
"prgs-reviewer": {
"base_url": "https://gitea.example.invalid",
"username": "<reviewer-username>",
"auth": { "type": "keychain", "id": "prgs-reviewer-token" },
"default_owner": "<owner>",
"execution_profile": "gitea-reviewer"
},
"prgs-author": {
"base_url": "https://gitea.example.invalid",
"username": "<author-username>",
"auth": { "type": "env", "name": "GITEA_TOKEN_PRGS_AUTHOR" },
"default_owner": "<owner>",
"execution_profile": "gitea-author"
}
}
}
version— canonical schema version (currently1).profiles— map of profile name → profile.auth— a reference, never an inline secret:- keychain:
{ "type": "keychain", "id": "<service-id>" }— the token is read from the macOS keychain on demand. - env:
{ "type": "env", "name": "<ENV_VAR_NAME>" }— the token is read from that environment variable.
- keychain:
Inline token/password keys are rejected. Token values are never stored in,
returned by, or logged from profile metadata. Precedence: explicit process env
vars override JSON profile values; the JSON profile fills only what the
environment leaves unset. With GITEA_MCP_CONFIG unset, behavior is exactly the
legacy environment-only mode.
Thin launcher pattern
An LLM MCP launcher (Claude / Gemini / Codex) contains only command, args,
and the two GITEA_MCP_* variables — never a token or password:
"gitea-tools": {
"command": "/path/to/Gitea-Tools/venv/bin/python3",
"args": ["/path/to/Gitea-Tools/mcp_server.py"],
"env": {
"GITEA_MCP_CONFIG": "/path/to/.config/gitea-tools/profiles.json",
"GITEA_MCP_PROFILE": "prgs-reviewer"
}
}
Dual-profile MCP launcher pattern (Recommended)
To avoid the bottleneck of relaunching/restarting the MCP server to switch between author and reviewer roles, the client should register both profiles concurrently as separate server instances in the client's MCP configuration:
"gitea-author": {
"command": "/path/to/Gitea-Tools/venv/bin/python3",
"args": ["/path/to/Gitea-Tools/mcp_server.py"],
"env": {
"GITEA_MCP_CONFIG": "/path/to/.config/gitea-tools/profiles.json",
"GITEA_MCP_PROFILE": "prgs-author"
}
},
"gitea-reviewer": {
"command": "/path/to/Gitea-Tools/venv/bin/python3",
"args": ["/path/to/Gitea-Tools/mcp_server.py"],
"env": {
"GITEA_MCP_CONFIG": "/path/to/.config/gitea-tools/profiles.json",
"GITEA_MCP_PROFILE": "prgs-reviewer"
}
}
- Tool Namespaces: Tool calls become distinct and identity-scoped in the client UI:
mcp__gitea-author__*(for creating issues, pushing branches, creating PRs)mcp__gitea-reviewer__*(for reviewing PRs, approving, requesting changes, merging)
- Trust Model: Separate tokens remain separate in the keychain/environment. Each instance operates under its own
GITEA_MCP_PROFILEand enforces its ownallowed_operations. A runtimewhoamiidentity check is still performed independently, and self-review/self-merge checks remain strictly mandatory. The dual-server pattern is a operational convenience and never a security bypass. - Reviewer-Identity PR Creation Deadlock: Reviewer/merge identities must not create PRs or push branches. Doing so makes the reviewer identity the PR author in Gitea, blocking subsequent independent review and causing a review deadlock. Normally, PRs must be created by the author/work identity (
gitea-author), leaving the reviewer identity (gitea-reviewer) clean and available for independent review and merge. - Reconciler namespace (#310): Register a third static instance for
already-landed PR cleanup when review queues block on open PRs whose heads
already landed on
master:
"gitea-reconciler": {
"command": "/path/to/Gitea-Tools/venv/bin/python3",
"args": ["/path/to/Gitea-Tools/mcp_server.py"],
"env": {
"GITEA_MCP_CONFIG": "/path/to/.config/gitea-tools/profiles.json",
"GITEA_MCP_PROFILE": "prgs-reconciler"
}
}
The reconciler profile grants gitea.pr.close only for
gitea_reconcile_already_landed_pr after ancestry proof — not for normal
review or author workflows.
- Fallback: If the dual-profile MCP launcher pattern is not supported or configured in the client, the LLM must relaunch or restart the client/MCP with the correct profile environment variable before claiming or working on any tasks.
Setup runbook — interactive menu
Create and manage profiles without hand-editing JSON:
./scripts/gitea-config-menu
Menu options: list / add / edit / remove profiles · validate config · test profile authentication · show authenticated user · generate launcher snippets (Claude/Gemini/Codex) · check reviewer eligibility for a PR.
In a real terminal the menu takes a single keypress (no Enter), Enter quits the main menu and cancels/back-outs of any submenu, and you pick a profile from a numbered list instead of typing its name. Non-interactive runs (pipes/tests) fall back to line input and never block.
Create an author + a reviewer profile:
add profile→ nameprgs-author, base URL, username, default owner/repo, execution profilegitea-author, auth typekeychainorenv.- keychain: store the token now (hidden prompt); it goes to the keychain
under an id like
prgs-author-token— never into the JSON. - env: record a var name like
GITEA_TOKEN_PRGS_AUTHOR; set that variable yourself in the environment.
- keychain: store the token now (hidden prompt); it goes to the keychain
under an id like
add profileagain → nameprgs-reviewer, execution profilegitea-reviewer. Existing profiles are preserved.validate config→ confirm no problems.generate launcher snippets→ paste the printed snippet into each LLM client's MCP config (it contains no secret).test profile authentication→ prints the resolved Gitea username (the only time an API call is made, and only on request).check reviewer eligibility for a PR→ enter a PR number; prints the authenticated user, the PR author, andELIGIBLE/INELIGIBLE. Read-only — it never approves or merges.
Migration runbook — away from duplicated credential blocks
Old setups duplicated GITEA_USER_*, GITEA_PASS_*, and GITEA_SITE_* across
every LLM's mcp_config.json — duplicating profiles and exposing secrets.
- For each instance/role, create one canonical profile (menu →
add profile), storing the secret in the keychain or an env var and referencing it by id/name only. validate config, thentest profile authenticationfor each profile.- Replace each LLM's server block with the thin launcher (command + args +
GITEA_MCP_CONFIG+GITEA_MCP_PROFILE). - Delete the
GITEA_USER_*/GITEA_PASS_*/GITEA_SITE_*blocks from every LLM config. - Rotate any token that previously sat in a client config.
Legacy environment-only setups keep working unchanged until migrated.
Workflow runbooks
Each runbook names the profile role it runs under, the steps, and a safe
prompt. Confirm the active profile first (gitea_get_profile / gitea_whoami).
Work Selection Rule for LLMs
Before starting any issue or PR work, acquire or verify a work lease. Do not begin coding, reviewing, fixing, branching, committing, pushing, commenting, or creating a PR until you prove the target is not already being worked.
Required checks:
- List open PRs.
- Search for PRs linked to the target issue.
- Search local and remote branches for the issue number.
- Search registered worktrees for the issue branch.
- Check dirty worktrees.
- Check active leases or recent handoffs.
- Check whether the issue was already completed by a merged PR.
If another active LLM/session owns the lease, stop. Allowed responses: continue as the lease owner; review the existing PR if reviewer capability allows; produce a handoff; request takeover after lease expiry; stop with "work already claimed."
Never create a parallel branch or PR for the same issue unless the old branch is proven abandoned and the takeover is recorded.
Gitea-Tools lease gates: gitea_lock_issue (fail-closed before author
mutations), status:in-progress, and claim comments. gitea_lock_issue
records an author_issue_work lease in a keyed lock file under
GITEA_ISSUE_LOCK_DIR (default ~/.cache/gitea-tools/issue-locks), one file
per remote + org + repo + issue_number. The current MCP session binds
its active lock through a per-process pointer so concurrent repos/issues never
share one overwrite-prone slot (#443).
Each lock payload includes issue number, optional PR number, branch, worktree path, claimant identity/profile, created timestamp, expiry timestamp, and last heartbeat timestamp. An active same-issue/same-operation lease blocks duplicate work. An expired lease still blocks takeover until a recovery review records why the prior work is abandoned, completed, or unsafe to continue.
Stacked PRs (#484). By default the lock worktree must be base-equivalent to
master/main/dev — ordinary work is unchanged. A stacked PR (deliberately
based on another unmerged PR's branch) is an explicit, opt-in path: pass
stacked_base_branch and stacked_base_pr to gitea_lock_issue. The lock
fails closed unless that branch is owned by a live open PR whose number
matches stacked_base_pr, so arbitrary or stale branches cannot be used as
bases. When approved, the lock payload records
approved_stacked_base = {branch, pr_number, verified_open} and the worktree may
be base-equivalent to that branch instead of master. gitea_create_pr then
allows base = <that branch> only when it matches the recorded approval, the
dependency PR is still open, and the PR body documents the stack:
Stacked on PR #<X> / issue #<Y>Base branch: <feature-branch>Head branch: <this-issue-branch>Do not merge before PR #<X>(merge ordering)- retarget/rebase to
masterafter the dependency lands, if required
Stacked support never bypasses the issue lock — the base is recorded on the
lock and re-verified at PR time. A merged/closed dependency base fails closed;
retarget onto master or re-lock against a live base.
Do not manually seed /tmp/gitea_issue_lock.json or any lock file as a normal
recovery path. That global slot is deprecated and can clobber unrelated live
leases (#438). After an MCP restart, call gitea_lock_issue again — own-branch
adoption rebinds the session when the issue's exact branch already exists (#442).
gitea_create_pr resolves the durable keyed lock by session pointer or by
matching head branch without unsafe manual seeding.
Issue-lock recovery (#447): Do not manually seed, restore, or delete
/tmp/gitea_issue_lock.json as a normal recovery path. That file is global
shared state and manual writes can clobber another session's live lease. Use
sanctioned recovery instead:
gitea_lock_issueon a cleanbranches/worktree (normal path).- Own-branch adoption via #442 when the issue's exact branch is already pushed.
- Operator override only when explicitly authorized — record
External-state mutationsandoperator override proofin the final report.
Adoption proof in the live lock response (#477): when gitea_lock_issue
adopts an existing own branch, the response carries an adoption block with
citable fields — adoption_decision (ADOPT), adopted (true),
adopted_branch, adopted_branch_head, matcher_summary (boundary-safe reason
the branch qualified), competing_branch_check, and safe_next_action. A normal
lock instead returns an adoption_check block with adoption_decision
(NO_MATCH) and adopted: false, so a non-adoption response can never be misread
as claiming adoption. Recovery reports should quote the live lock response
adoption/adoption_check block directly instead of inferring adoption from
separate offline checks.
gitea_create_pr rejects lock files that lack sanctioned lock_provenance
metadata. Final-report validation blocks handoffs that hide lock read/write/delete
under External-state mutations: none or mix author PR creation with reviewer
approval in one run. See also #438 (global lock redesign).
Remote branches matching the issue number are also treated as active work unless the recovery review proves the branch is abandoned or superseded. Never delete or clean up a branch when it has an active lease, dirty worktree, open PR, or is the only copy of unmerged work.
Full portable wording:
skills/llm-project-workflow/SKILL.md.
Global LLM Worktree Rule
The main project checkout is a stable control checkout. It must stay on the
configured stable branch: master, main, or dev.
All LLM task work must happen inside the project's branches/ directory.
Before any mutation, prove:
- current project root
- current working directory
- current branch
- stable branch for the main checkout
- session-owned worktree path under
branches/
If cwd is not inside branches/, stop. Do not edit, create, delete, format,
test-write, commit, merge, rebase, checkout task branches, resolve conflicts,
or run cleanup.
There are no exceptions for small fixes, docs, tests, cleanup, PR review fixes, conflict resolution, or emergencies.
The main checkout may only be used for read-only inspection, fetching,
stable-branch update after merged PRs, creating branches/ worktrees, or
explicit control-checkout repair.
Portable wording: skills/llm-project-workflow/SKILL.md.
Root checkout guard (#475)
The MCP server enforces a fail-closed root checkout guard before author,
reviewer, and merger mutations when the active workspace is not an isolated
branches/... worktree (reconciler close paths remain exempt per #468).
The guard blocks when the control checkout (repository root) is:
- on a non-stable branch (
master/main/devexpected), - detached HEAD,
- dirty (tracked edits),
- or its
HEADdoes not matchprgs/masterwhen that ref is available.
Remediation (never auto-reset or stash):
Root checkout is not on master. Preserve state, switch root back to master, and use
scripts/worktree-reviewor the sanctioned issue worktree flow.
Recovery after root hijack:
- Preserve any in-progress edits (copy paths, note branch name, or commit on a
rescue branch from a
branches/...worktree). - From the repository root:
git checkout master(ormain/devper repo policy) andgit fetch prgs && git merge --ff-only prgs/masterwhen safe. - Confirm
git statusis clean andgit branch --show-currentismaster. - Resume work only inside
branches/issue-<n>-<slug>viagitea_lock_issue/git worktree add.
branches/... directories are disposable role worktrees; the root checkout is
the stable orchestration surface only.
Bootstrap Review Path for self-hosted MCP fixes (#557)
When a PR fixes Gitea-Tools MCP workflow code that the live daemon still
runs from broken master, canonical review can deadlock on itself.
Do not improvise raw API, direct imports, root edits, or gate bypasses.
Use the narrow controller-authorized path documented in:
Hard rules:
- No merge without a durable
BOOTSTRAP REVIEW AUTHORIZATION (#557)record on the PR or linked issue. - Validation, duplicate-PR, mutation-ledger, and contamination audits remain mandatory.
- Root checkout stays read-only orchestration only; verification runs in
branches/worktrees. - After land: restart MCP daemons and re-verify with canonical tools only.
- This never weakens normal reviewer/merger gates for ordinary PRs.
Shell Spawn Hard-Stop Rule
Symptom: a shell tool call returns exit_code: -1 with empty stdout/stderr.
That is an executor spawn failure, not a command failure — the command never
ran, and retrying the identical call cannot succeed.
Required behavior (fail closed, issue #258):
- Probe once. On the first spawn failure, run one trivial probe
(
echo okorpwd). If the probe also returnsexit_code: -1, mark shell unavailable for the session. - Hard-stop at two. After two consecutive spawn failures, stop all
further shell tool use for the session; never retry the same failing
spawn. A hundred retries produce a hundred identical failures (session
019f382e: 100+ tool calls stalled on a trivial encode-and-commit task). - Emit a recovery report. The report must direct the operator to:
- restart the session,
- kill hung background terminals (a hung test runner holding the executor is a known contributor),
- prefer MCP-native paths for remaining mutations (for example
gitea_commit_filesundergitea.repo.commit) instead of shell.
- No improvised fallbacks. Shell unavailability never authorizes WebFetch/browser/manual-encoding workarounds (see #260). No shell means stop-and-report.
Doc-contract tests: tests/test_shell_spawn_hard_stop_docs.py.
Subagent Tool-Budget Guardrails
General-purpose subagents tasked with deterministic MCP work (for example a
single gitea_commit_files call) have expanded to 100–122 tool calls,
WebFetch/Playwright fallbacks, and throwaway helper-script generation instead of
calling the native MCP tool once (observed during #152 closure, issue #259).
Default budgets (fail closed when exceeded):
| Task class | Max tool calls | Max wall time |
|---|---|---|
Single-step MCP mutation (commit_files, create_pr, lock_issue) |
15 | 5 minutes |
| Review / merge queue inspection | 40 | 15 minutes |
| Exploration / codebase search (non-mutating) | 60 | 20 minutes |
Required behavior:
- Main session first. When the active author profile allows
gitea.repo.commitandgitea_commit_filesis visible, the main session must call it directly — do not delegate commit authority to a subagent (see #260). - Native MCP before fallback. After a shell spawn failure (#258), attempt the native MCP tool once before any alternate path. Shell unavailability never authorizes WebFetch, Playwright, or manual base64 encoding.
- No retry spirals. Never resume a failed subagent into a larger retry loop or spawn a second subagent for the same deterministic step. Stop and emit a recovery report instead.
- Forbidden detours when
gitea_commit_filesis available: WebFetch, Playwright/browser automation, manual LLM-generated base64, and ad-hoc_encode_*/_emit_*helper scripts left in the repo.
Doc-contract tests: tests/test_subagent_tool_budget_docs.py.
Branch worktree isolation
All LLM implementation and review work happens in an isolated branch worktree
under branches/. The main repository checkout is an orchestration checkout:
use it for status checks, issue creation/claiming, and creating worktrees, but
do not edit tracked repository files there.
Issue → branch → worktree → PR → cleanup. Every implementation branch is tied to an issue number so the work is traceable end to end:
| Stage | Form |
|---|---|
| Issue | #123 (claimed with status:in-progress) |
| Branch | (fix|feat|docs|chore)/issue-123-<slug> (review: review/pr-456-<slug>) |
| Worktree | branches/fix-issue-123-<slug> (slashes → hyphens) |
| PR | body says Closes #123 or Fixes #123 (closes issue); Implements #123 or Refs #123 (does NOT close) |
| Cleanup | remove remote+local branch + worktree folder; drop status:in-progress |
scripts/worktree-start rejects implementation branches that are not
issue-linked (use --allow-unlinked only for genuine exceptions). When claiming,
post a comment like
Claimed. Branch: fix/issue-123-<slug>. Worktree: branches/fix-issue-123-<slug>.
Gitea has no native issue→branch API field (only a PR's head branch), so this
linkage is enforced by branch name + claim comment + PR body + cleanup.
Branch folders are ignored by git via branches/, so dirty work in one issue
does not block starting an unrelated issue in a separate branch folder. No LLM
may edit another issue's branch folder unless explicitly assigned to that issue.
No LLM may clean another issue's branch folder unless the PR is merged or closed
and cleanup is explicitly part of the task.
Agent temp artifact cleanup (#261)
Failed or aborted MCP commit attempts sometimes leave throwaway helper scripts in
the repository root. These are not part of any issue scope and pollute
git status, which can break gitea_lock_issue and preflight checks.
Patterns (repo root only, untracked):
_encode_*.py— base64 payload encoders_emit_*.py— commit payload emitters_inline_*.py— inline encoding helpers
Required cleanup (after MCP commit completes or aborts):
- Delete any matching files at the repo root (
rm ./_encode_*.pyetc.). - Confirm
git statusis clean on the orchestration checkout beforegitea_lock_issue. - Prefer native
gitea_commit_files/ gated commit paths — do not leave shell encoding fallbacks behind.
Root-level matches are listed in .gitignore so they never get committed.
gitea_get_runtime_context and gitea_lock_issue surface warnings (not
hard blocks) when these artifacts are still present.
Implementation work and review work must use separate branch folders. For
example, an implementation branch might live under
branches/fix-issue-123-example, while a review branch for the resulting PR
uses its own folder.
Issue creation and claiming may happen from the orchestration checkout:
- Create or identify the tracking issue.
- Claim it with
status:in-progress. - Create the issue branch worktree.
cdinto the branch worktree and perform all file edits there.
Preferred helper:
scripts/worktree-start fix/issue-123-example
cd branches/fix-issue-123-example
Because venv/ is ignored and not copied into new worktrees, run checks with a
known Python interpreter. Either create a venv inside the branch folder, or use
the orchestration checkout's venv by explicit path.
Equivalent manual commands:
git fetch prgs --prune
git worktree add -b fix/issue-123-example branches/fix-issue-123-example prgs/master
cd branches/fix-issue-123-example
For review work, create a separate detached review worktree instead of reusing the author's implementation folder:
scripts/worktree-review fix/issue-123-example # → branches/review-fix-issue-123-example
Cleanup is explicit and only after merge or close. Use the helper (it fetches/ prunes first, refuses to remove a dirty worktree, and only safe-deletes a merged branch), or the equivalent manual commands:
scripts/worktree-clean --delete-branch fix/issue-123-example
# equivalent manual commands:
cd <main-repo>
git fetch prgs --prune
git worktree remove branches/fix-issue-123-example
git branch -d fix/issue-123-example
All three helpers accept --dry-run to print the exact commands/paths without
touching anything.
MCP-native commit path (#260)
When the active author profile allows gitea.repo.commit and
gitea_commit_files is visible in the client, that is the only approved
path for committing files to the tracked repository. Do not improvise alternate
encoding or transport when MCP commit is available.
Required before commit:
- Call
gitea_resolve_task_capabilityforcommit_filesorgitea_commit_filesand confirmallowed_in_current_sessionis true. - Use
gitea_commit_fileswith file payloads prepared in the author worktree. - Stage only issue-scoped paths; never commit throwaway
_encode_*/_emit_*/_inline_*helpers.
Explicitly forbidden workarounds when MCP commit is reachable:
WebFetch/ HTTP calls to external decode sites (for example httpbin base64 endpoints)- Playwright or other browser automation to bypass MCP
- Manual LLM-generated base64 pasted into ad-hoc scripts
- Delegating commit authority to a subagent while the main session has
gitea.repo.commiton an author profile
If shell encoding is unavailable (spawn failure, hung terminal) and MCP commit cannot run: stop with a recovery report. Mention restarting the session, clearing hung background terminals, switching to MCP-native commit, and the agent temp artifact cleanup checklist. Do not retry shell encoding in a loop and do not substitute WebFetch/Playwright/manual base64.
Create an issue / child issues
- Profile: issue-manager or author (any profile allowed to create issues).
- Steps: create the parent/roadmap issue; create child issues; apply the minimal label set; link children to the parent.
- Prompt: `Using the issue-manager profile, create issue "