Agent workflow
SkillMediaMulti-agent workflow contract for this repo, orchestrator vs implementation agents, GitHub label state machine, PR lifecycle, the read-only al-language corpus and how a run resolves it, and the expectations-manifest path for OOS-by-design tests. Use when acting as orchestrator/impl-agent without the dedicated sub-agent, when triaging the issue/PR queue manually, or when deciding whether an issue is a runner gap, an OOS-by-design declaration, or a corpus bug to upstream.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Agent workflow skill
What this skill tells your AI
The instructions your AI receives, as published by stefanmaron/businesscentral.al.runner in .claude/skills/al-runner-workflow/SKILL.md and read by ahel’s review.
This repository uses a multi-agent workflow. Agents are identified by GitHub issue / PR labels.
Identity
Your agent identity (impl-1, impl-2, orchestrator) is given in the task prompt. It maps to a GitHub label (agent: impl-1, etc.).
Implementation agent loop
If you are impl-1 or impl-2:
- Check for issues labeled
agent: <your-id>ANDstatus: in-progress— that is your active issue if one exists. - If no active issue, find the next unclaimed issue:
status: readywith noagent:label and no human assignee. Claim it: addagent: <your-id>,status: in-progress, assignee@me. Removestatus: ready. - Verify you understand the AL pattern. If the issue body lacks a runnable AL reproducer or a specific failing assertion, do not guess. Add
status: needs-input, ask the reporter, stop (.claude/rules/no-assumption-fixes.md). - Branch
agent/<your-id>/issue-<N>, pushed, with a draft PR carryingCloses #N, the labelagent: <your-id>, assigned to@me(.claude/agents/impl-agent.md, Step 2). - Implement red → green (
.claude/rules/tdd.md). The right test depends on what kind of issue this is — see "Issue kinds" below. - Rewrite the body, mark the draft ready, label
status: review-ready. - Return. The coordinator reads CI and resumes you, or dispatches a fresh agent onto the PR, when it goes red.
- Auto-merge fires when approved + green (
allow_auto_merge=trueis a repo setting, not visible in the checkout). Return to step 1.
One issue at a time per impl agent. No second claim while a PR is open.
Issue kinds — where does the test go?
| Kind | Test lives in | Notes |
|---|---|---|
| Runner gap on an in-scope AL pattern | An al-language test that fails before, passes after. If the corpus does not cover the pattern, write the upstream test first in StefanMaron/BusinessCentral.AL.Language.Tests and cite it with a Corpus-PR: line — there is no pin to bump (#3737); that line is what makes your PR's matrix resolve the corpus at your corpus PR's branch head. | Most common. The corpus must validate against real BC before the runner can claim parity. |
| Test is OOS-by-design (SMTP, real HTTP, …) | New entry in tests/expectations/oos-<area>.json per docs/expectations.md. | The runner must throw RunnerOutOfScopeException with the reason from docs/scope.md. .claude/rules/loud-failures.md. |
| In-scope but not yet implemented | tests/expectations/known-gaps-<area>.json entry linking the GH issue. | Transient — entry is removed when the gap is closed. |
| Runner-specific positive assertion | New suite under tests/runner-extras/. | E.g. "calling X throws OOS with reason Y". |
| Corpus bug (test mis-asserts something real BC also fails) | Upstream PR against the corpus. | Do not edit the corpus from this repo. |
tests/al-language/ is read-only, and since #3737 it is not in git at all: it is resolved per
run — at master, or at the head of the corpus pull request a PR body's Corpus-PR: line names
— and every run prints corpus: <full sha> (<ref>). Locally, tools/corpus-checkout.py puts it
there. Never edit it. See .claude/rules/al-language-submodule.md.
Orchestrator loop (priority order)
If you are orchestrator:
- PRs first. Find PRs labeled
status: review-ready. CI green + no unresolved threads + noCHANGELOG.mdin diff + no edits undertests/al-language/+ relevant expectation entries / runner-extras tests cited in the body + every condition of the arming list inorchestrating-a-session("A reviewer that approves a PR arms auto-merge") → approve and squash-merge (gh pr merge --auto --squash). Whereghis absent (web and remote sessions,.claude/rules/github-access.md) the verdict cannot be read: review, comment, and hold. Otherwise leave actionable review comments. - Unblock. Review
status: blockedissues; resolve if possible. - Triage of new untriaged issues is owned by the
triagersub-agent (Opus), which runs at the start of a cycle and setsstatus: readyvs.status: needs-input. The orchestrator does not triage.
Workers self-select from the status: ready queue. The orchestrator does not assign issues to specific workers.
GitHub access: operation → tool map
.claude/rules/github-access.md covers detecting whether gh is available.
Once detected, here is which tool covers which operation:
| Operation | gh | MCP tool |
|---|---|---|
| Who am I | gh api user --jq .login | mcp__github__get_me |
| List issues | gh issue list | mcp__github__list_issues |
| Read issue / its comments | gh issue view | mcp__github__issue_read (get, get_comments) |
| Label / assign / close issue | gh issue edit, gh issue close | mcp__github__issue_write (method: update) |
| Comment on issue or PR | gh issue comment, gh pr comment | mcp__github__add_issue_comment (PRs too — pass the PR number) |
| List PRs | gh pr list | mcp__github__list_pull_requests |
| PR detail / diff / files / CI | gh pr view, gh pr diff, tools/ci-wait.py <N> --timeout 0 | mcp__github__pull_request_read (get, get_diff, get_files); get_check_runs reads checks but is not the verdict (ci-wait.py needs gh), so without gh a PR is reviewed and held, never armed |
| Merge a PR | gh pr merge --squash | mcp__github__merge_pull_request (merge_method: "squash") |
| Open a PR | gh pr create | mcp__github__create_pull_request |
| Label a PR | gh pr edit --add-label | mcp__github__update_pull_request |
| Edit a PR body | tools/pr-body.py <N> --replace OLD NEW (never a hand-rolled fetch/modify/upload — see .claude/rules/branch-and-pr.md) | mcp__github__update_pull_request after reading the body back, with the same care |
| Read failing CI logs | gh run view --log-failed — empty output is a refusal, not an empty log; see .claude/rules/ci-verdicts.md §3 | mcp__github__get_job_logs (failed_only: true, return_content: true) |
| Search for duplicates | gh issue list --search | mcp__github__search_issues |
Any agent definition granting Bash for GitHub work must also grant the
mcp__github__* tools it needs (plus ToolSearch, since those tools are
deferred) in its tools: frontmatter — otherwise the fallback is unavailable
precisely where it is needed.
Concurrency with human maintainers
The GitHub assignee field is the boundary between agent-owned and human-owned work:
- When an impl agent claims an issue, it assigns
@mealongside the labels. PRs the bot opens are also assigned to@me. - Every agent (triager, orchestrator, impl) skips any issue or PR whose assignee is a user other than
@me— a human maintainer is on it. - A human can take over an in-flight agent task by re-assigning the issue / PR; agents back off on their next pass.
Hard rules (all agents)
- Never push directly to
main— always via PR. - Never touch an issue or PR assigned to a non-
@meuser. - Impl agents never self-assign work outside the orchestrator queue.
- Branch name:
agent/<agent-id>/issue-<N>— no exceptions. - PR body must contain
Closes #N. - Set
status: review-readyon the PR when you mark it ready; the coordinator reads CI. - One PR at a time per impl agent.
- Never edit
CHANGELOG.md. - Never edit a file inside
tests/al-language/— it is gitignored and resolved per run, so a diff cannot legitimately reach it. Cite a corpus change with aCorpus-PR:line instead (al-language-submodule.md). - Honour the precompiled-DLL contract (
.claude/rules/precompiled-dll-respect.md) and loud-failures rule (.claude/rules/loud-failures.md). --repo StefanMaron/BusinessCentral.AL.Runneron everyghcommand when running outside the repo's default.
Label state machine
| Label | Meaning |
|---|---|
status: ready | Unclaimed, ready for an impl agent to pick up |
status: in-progress | Currently being worked on by the labeled agent: * |
status: review-ready | PR is marked ready; the orchestrator reads CI and reviews |
status: blocked | Needs human or cross-issue input |
status: needs-input | Issue body too thin to identify root cause; reporter must elaborate (set by triager — see no-assumption-fixes) |
agent: impl-1 / agent: impl-2 | Identity claim on an issue or PR |
Sister docs
docs/expectations.md— schema and modes fortests/expectations/docs/scope.md— per-API in/out-of-scope reasons.claude/rules/branch-and-pr.md.claude/rules/al-language-submodule.md.claude/rules/precompiled-dll-respect.md.claude/rules/loud-failures.md.claude/rules/tdd.md.claude/rules/no-assumption-fixes.md.claude/rules/no-changelog-edits.md.claude/rules/file-issues-for-gaps.md
Signals
- GitHub stars
- 36
- Forks
- 13
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
al-runner-workflow- Source
- github.com/stefanmaron/businesscentral.al.runner