feat: mount canonical gitea-workflow skill for Codex and MCP inventory (Closes #551) #565
@@ -413,6 +413,24 @@ The guard blocks when the **control checkout** (repository root) is:
|
||||
`branches/...` directories are disposable role worktrees; the root checkout is
|
||||
the stable orchestration surface only.
|
||||
|
||||
## Canonical workflow skill names (#551)
|
||||
|
||||
Controller prompts and sessions must load the **same** workflow skill wall
|
||||
regardless of runtime (Claude, Codex, Gemini):
|
||||
|
||||
| Name | Role |
|
||||
|------|------|
|
||||
| `gitea-workflow` | Primary controller / Codex skill name |
|
||||
| `llm-project-workflow` | Portable in-repo package |
|
||||
| `git-pr-workflows` | Legacy alias |
|
||||
|
||||
- Inventory: `mcp_list_project_skills` lists all three.
|
||||
- Preflight: `mcp_check_workflow_skill_preflight` before mutations.
|
||||
- Codex install: `scripts/install-codex-workflow-skill.sh`
|
||||
- Full doc: [`docs/workflow-skill-mount.md`](workflow-skill-mount.md)
|
||||
|
||||
If the skill is missing, stop with BLOCKED + DIAGNOSE — do not mutate.
|
||||
|
||||
## No direct-import mutation path (#558)
|
||||
|
||||
Never `import gitea_mcp_server` or call `gitea_auth.get_auth_header` /
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
# Workflow skill mount across runtimes (#551)
|
||||
|
||||
## Problem
|
||||
|
||||
Controller prompts require **`gitea-workflow`**, but:
|
||||
|
||||
- Claude may load `~/.claude/skills/gitea-workflow`
|
||||
- Codex often has **no** `~/.codex/skills/gitea-workflow`
|
||||
- The portable package in-repo is `skills/llm-project-workflow`
|
||||
- `mcp_list_project_skills` historically listed operational guides only, not
|
||||
the workflow router
|
||||
|
||||
Sessions then either **block** incorrectly or **proceed without** the workflow
|
||||
wall.
|
||||
|
||||
## Canonical names (must resolve to the same skill)
|
||||
|
||||
| Name | Use |
|
||||
|------|-----|
|
||||
| `gitea-workflow` | **Primary** controller / Codex skill name |
|
||||
| `llm-project-workflow` | Portable in-repo package name |
|
||||
| `git-pr-workflows` | Legacy alias |
|
||||
|
||||
Source of truth: `skills/llm-project-workflow/SKILL.md`
|
||||
In-repo alias stub: `skills/gitea-workflow/SKILL.md`
|
||||
|
||||
## Codex install
|
||||
|
||||
From a `branches/` worktree (or any clone of the repo):
|
||||
|
||||
```bash
|
||||
./scripts/install-codex-workflow-skill.sh
|
||||
# optional:
|
||||
./scripts/install-codex-workflow-skill.sh --dry-run
|
||||
./scripts/install-codex-workflow-skill.sh --skills-dir "$HOME/.codex/skills"
|
||||
```
|
||||
|
||||
This symlinks the portable package under all three names. **Restart Codex**
|
||||
after install.
|
||||
|
||||
## MCP discovery
|
||||
|
||||
- `mcp_list_project_skills` includes `gitea-workflow`, `llm-project-workflow`,
|
||||
and `git-pr-workflows`.
|
||||
- `mcp_get_skill_guide("<name>")` returns the same router steps for each.
|
||||
- `mcp_check_workflow_skill_preflight` proves the in-repo skill file exists and
|
||||
reports Codex mount status.
|
||||
|
||||
## Preflight rule
|
||||
|
||||
Before any git or Gitea mutation:
|
||||
|
||||
1. Call `mcp_check_workflow_skill_preflight`.
|
||||
2. If `blocked` / `workflow_skill_ready` is false → **BLOCKED + DIAGNOSE**; do
|
||||
not mutate.
|
||||
3. Load the skill by **any** canonical name and follow the router.
|
||||
|
||||
Missing Codex mount alone does not block if the in-repo skill is present and
|
||||
loaded via MCP/docs; operators should still install the Codex symlink so
|
||||
prompt names resolve natively.
|
||||
|
||||
## Controller prompts
|
||||
|
||||
Prefer:
|
||||
|
||||
> Invoke skill `gitea-workflow` (alias of `llm-project-workflow`).
|
||||
|
||||
Do not require a name that is only available on one runtime.
|
||||
+37
-1
@@ -828,6 +828,7 @@ import reconciliation_workflow # noqa: E402
|
||||
import audit_reconciliation_mode # noqa: E402
|
||||
import review_merge_state_machine # noqa: E402
|
||||
import pr_work_lease # noqa: E402
|
||||
import workflow_skill # noqa: E402
|
||||
import conflict_fix_classification # noqa: E402
|
||||
import native_mcp_preference # noqa: E402
|
||||
import master_parity_gate # noqa: E402
|
||||
@@ -7273,6 +7274,9 @@ _PROJECT_SKILLS = {
|
||||
},
|
||||
}
|
||||
|
||||
# Canonical workflow router skill names for Claude/Codex/Gemini parity (#551).
|
||||
_PROJECT_SKILLS.update(workflow_skill.workflow_skill_registry_entries())
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def mcp_get_control_plane_guide(
|
||||
@@ -7394,7 +7398,7 @@ def mcp_get_control_plane_guide(
|
||||
|
||||
@mcp.tool()
|
||||
def mcp_list_project_skills() -> dict:
|
||||
"""List the project's workflow skills and when to use them (#128).
|
||||
"""List the project's workflow skills and when to use them (#128, #551).
|
||||
|
||||
Read-only; makes no API calls. Each skill reports its name,
|
||||
description, when-to-use guidance, required operations, status
|
||||
@@ -7402,6 +7406,9 @@ def mcp_list_project_skills() -> dict:
|
||||
'operator-only'), and whether the current profile's operations permit it. Use
|
||||
mcp_get_skill_guide(name) for the step-by-step guide.
|
||||
|
||||
Includes the canonical workflow router under gitea-workflow,
|
||||
llm-project-workflow, and git-pr-workflows (#551).
|
||||
|
||||
Returns:
|
||||
dict with 'read_only', 'count', and 'skills' (list). Never secrets.
|
||||
"""
|
||||
@@ -7426,10 +7433,39 @@ def mcp_list_project_skills() -> dict:
|
||||
}
|
||||
if meta.get("notes"):
|
||||
entry["notes"] = meta["notes"]
|
||||
if meta.get("repo_path"):
|
||||
entry["repo_path"] = meta["repo_path"]
|
||||
if meta.get("canonical"):
|
||||
entry["canonical"] = True
|
||||
skills.append(entry)
|
||||
return {"read_only": True, "count": len(skills), "skills": skills}
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def mcp_check_workflow_skill_preflight(
|
||||
project_root: str | None = None,
|
||||
) -> dict:
|
||||
"""Fail-closed preflight for the canonical workflow skill wall (#551).
|
||||
|
||||
Verifies skills/llm-project-workflow/SKILL.md exists in the project tree
|
||||
and reports whether Codex has a matching mount under ~/.codex/skills.
|
||||
Call before git/Gitea mutations when the controller requires gitea-workflow.
|
||||
|
||||
Args:
|
||||
project_root: Optional override; defaults to this MCP process root.
|
||||
|
||||
Returns:
|
||||
dict with workflow_skill_ready, blocked, canonical_names, codex mount
|
||||
status, and safe_next_action. Never secrets.
|
||||
"""
|
||||
root = (project_root or PROJECT_ROOT).strip()
|
||||
result = workflow_skill.assess_workflow_skill_presence(root)
|
||||
result["success"] = not result.get("blocked")
|
||||
result["project_root"] = root
|
||||
result["read_only"] = True
|
||||
return result
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def mcp_get_skill_guide(skill_name: str) -> dict:
|
||||
"""Step-by-step guide for one named project skill (#128).
|
||||
|
||||
Executable
+82
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env bash
|
||||
# Install canonical Gitea workflow skill into Codex skills dir (#551).
|
||||
# Symlinks the in-repo skills/llm-project-workflow package under the names
|
||||
# gitea-workflow, llm-project-workflow, and git-pr-workflows.
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
usage: scripts/install-codex-workflow-skill.sh [--dry-run] [--skills-dir DIR]
|
||||
|
||||
Symlink the portable skills/llm-project-workflow package into the Codex
|
||||
skills directory under the canonical names:
|
||||
gitea-workflow
|
||||
llm-project-workflow
|
||||
git-pr-workflows
|
||||
|
||||
Defaults:
|
||||
skills dir: $CODEX_HOME/skills or ~/.codex/skills
|
||||
source: <repo>/skills/llm-project-workflow
|
||||
EOF
|
||||
}
|
||||
|
||||
dry_run=0
|
||||
skills_dir=""
|
||||
while [[ "${1:-}" == --* ]]; do
|
||||
case "$1" in
|
||||
--dry-run) dry_run=1 ;;
|
||||
--skills-dir)
|
||||
shift
|
||||
skills_dir="${1:-}"
|
||||
;;
|
||||
--help|-h) usage; exit 0 ;;
|
||||
*) usage >&2; exit 2 ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
repo_root="$(cd "$script_dir/.." && pwd)"
|
||||
source_skill="$repo_root/skills/llm-project-workflow"
|
||||
|
||||
if [[ ! -f "$source_skill/SKILL.md" ]]; then
|
||||
echo "Error: missing $source_skill/SKILL.md" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "$skills_dir" ]]; then
|
||||
if [[ -n "${CODEX_HOME:-}" ]]; then
|
||||
skills_dir="$CODEX_HOME/skills"
|
||||
else
|
||||
skills_dir="${HOME}/.codex/skills"
|
||||
fi
|
||||
fi
|
||||
|
||||
names=(gitea-workflow llm-project-workflow git-pr-workflows)
|
||||
|
||||
echo "source: $source_skill"
|
||||
echo "codex skills dir: $skills_dir"
|
||||
|
||||
if [[ "$dry_run" -eq 0 ]]; then
|
||||
mkdir -p "$skills_dir"
|
||||
fi
|
||||
|
||||
for name in "${names[@]}"; do
|
||||
target="$skills_dir/$name"
|
||||
if [[ "$dry_run" -eq 1 ]]; then
|
||||
echo "dry-run: ln -sfn $source_skill $target"
|
||||
continue
|
||||
fi
|
||||
if [[ -e "$target" || -L "$target" ]]; then
|
||||
if [[ -L "$target" ]]; then
|
||||
rm -f "$target"
|
||||
else
|
||||
echo "Error: $target exists and is not a symlink (refuse to overwrite)" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
ln -sfn "$source_skill" "$target"
|
||||
echo "linked $target -> $source_skill"
|
||||
done
|
||||
|
||||
echo "done. Restart Codex so skills reload."
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
name: gitea-workflow
|
||||
description: >-
|
||||
Canonical alias for the Gitea/LLM project workflow router. Same skill as
|
||||
llm-project-workflow. Use at the start of any Gitea-Tools author, review,
|
||||
merge, reconcile, or issue-filing task. Trigger terms: gitea-workflow,
|
||||
llm-project-workflow, git-pr-workflows, gitea, PR review, work-issue.
|
||||
---
|
||||
|
||||
# gitea-workflow (canonical alias)
|
||||
|
||||
This directory is the **stable name** for controller prompts and Codex mounts
|
||||
(`gitea-workflow`). The portable implementation lives at:
|
||||
|
||||
**[`../llm-project-workflow/SKILL.md`](../llm-project-workflow/SKILL.md)**
|
||||
|
||||
## Required action
|
||||
|
||||
1. Open and follow `skills/llm-project-workflow/SKILL.md` (router).
|
||||
2. Load the matching workflow under `skills/llm-project-workflow/workflows/`.
|
||||
3. Do not mutate git/Gitea until the workflow is loaded.
|
||||
|
||||
## Multi-runtime names (must resolve identically)
|
||||
|
||||
| Name | Role |
|
||||
|------|------|
|
||||
| `gitea-workflow` | Canonical controller / Codex skill name |
|
||||
| `llm-project-workflow` | Portable in-repo skill package |
|
||||
| `git-pr-workflows` | Legacy alias |
|
||||
|
||||
Install for Codex:
|
||||
|
||||
```bash
|
||||
./scripts/install-codex-workflow-skill.sh
|
||||
```
|
||||
|
||||
Preflight via MCP: `mcp_check_workflow_skill_preflight`.
|
||||
@@ -6,6 +6,8 @@ description: >-
|
||||
report schema. Use at the start of any implementation, review, merge,
|
||||
reconciliation, or issue-filing task.
|
||||
---
|
||||
> **Also known as:** `gitea-workflow`, `git-pr-workflows` (canonical multi-runtime names — see docs/workflow-skill-mount.md / #551).
|
||||
|
||||
|
||||
# LLM Project Workflow Skill
|
||||
|
||||
|
||||
@@ -56,6 +56,10 @@ EXPECTED_SKILLS = [
|
||||
"jenkins-mcp",
|
||||
"glitchtip-mcp",
|
||||
"release-operator",
|
||||
# Canonical workflow router (#551) — same skill, multi-runtime names
|
||||
"gitea-workflow",
|
||||
"llm-project-workflow",
|
||||
"git-pr-workflows",
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
"""Tests for canonical workflow skill naming and preflight (#551)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
import workflow_skill
|
||||
import mcp_server
|
||||
|
||||
|
||||
class TestWorkflowSkillModule(unittest.TestCase):
|
||||
def test_canonical_names_include_gitea_workflow(self):
|
||||
names = workflow_skill.CANONICAL_WORKFLOW_SKILL_NAMES
|
||||
self.assertIn("gitea-workflow", names)
|
||||
self.assertIn("llm-project-workflow", names)
|
||||
self.assertIn("git-pr-workflows", names)
|
||||
|
||||
def test_repo_skill_present_in_this_checkout(self):
|
||||
root = str(Path(__file__).resolve().parent.parent)
|
||||
result = workflow_skill.assess_workflow_skill_presence(root)
|
||||
self.assertTrue(result["repo_skill_present"])
|
||||
self.assertTrue(result["repo_alias_present"])
|
||||
self.assertTrue(result["workflow_skill_ready"])
|
||||
self.assertFalse(result["blocked"])
|
||||
|
||||
def test_missing_repo_skill_blocks(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
result = workflow_skill.assess_workflow_skill_presence(tmp)
|
||||
self.assertTrue(result["blocked"])
|
||||
self.assertFalse(result["workflow_skill_ready"])
|
||||
self.assertTrue(any("missing" in r for r in result["reasons"]))
|
||||
|
||||
def test_codex_mount_detection(self):
|
||||
root = str(Path(__file__).resolve().parent.parent)
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
skill_dir = Path(tmp) / "gitea-workflow"
|
||||
skill_dir.mkdir()
|
||||
(skill_dir / "SKILL.md").write_text("# x\n", encoding="utf-8")
|
||||
result = workflow_skill.assess_workflow_skill_presence(
|
||||
root, codex_skills_dir=tmp
|
||||
)
|
||||
self.assertTrue(result["codex_skill_mounted"])
|
||||
self.assertTrue(result["codex_skill_mounts"]["gitea-workflow"])
|
||||
|
||||
def test_registry_entries_cover_aliases(self):
|
||||
entries = workflow_skill.workflow_skill_registry_entries()
|
||||
for name in workflow_skill.CANONICAL_WORKFLOW_SKILL_NAMES:
|
||||
self.assertIn(name, entries)
|
||||
self.assertEqual(entries[name]["status"], "available")
|
||||
|
||||
|
||||
class TestWorkflowSkillMcpTools(unittest.TestCase):
|
||||
def test_list_includes_canonical_names(self):
|
||||
with patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
"GITEA_PROFILE_NAME": "author-test",
|
||||
"GITEA_ALLOWED_OPERATIONS": "gitea.read",
|
||||
"GITEA_FORBIDDEN_OPERATIONS": "",
|
||||
},
|
||||
clear=False,
|
||||
):
|
||||
result = mcp_server.mcp_list_project_skills()
|
||||
names = {s["name"] for s in result["skills"]}
|
||||
for name in workflow_skill.CANONICAL_WORKFLOW_SKILL_NAMES:
|
||||
self.assertIn(name, names)
|
||||
|
||||
def test_get_skill_guide_gitea_workflow(self):
|
||||
guide = mcp_server.mcp_get_skill_guide("gitea-workflow")
|
||||
self.assertTrue(guide["success"])
|
||||
self.assertTrue(guide["steps"])
|
||||
self.assertIn("workflow", " ".join(guide["steps"]).lower())
|
||||
|
||||
def test_preflight_tool_ok_on_repo(self):
|
||||
result = mcp_server.mcp_check_workflow_skill_preflight()
|
||||
self.assertTrue(result["success"])
|
||||
self.assertTrue(result["workflow_skill_ready"])
|
||||
self.assertFalse(result["blocked"])
|
||||
self.assertIn("gitea-workflow", result["canonical_names"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,160 @@
|
||||
"""Canonical workflow skill naming and preflight for multi-runtime agents (#551).
|
||||
|
||||
Controller prompts and Claude historically require ``gitea-workflow``. The
|
||||
portable in-repo skill is ``skills/llm-project-workflow``. Codex must resolve
|
||||
the same canonical names so sessions do not proceed without the workflow wall.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
# Canonical names that all runtimes (Claude, Codex, Gemini, MCP inventory)
|
||||
# must treat as the same workflow router skill.
|
||||
CANONICAL_WORKFLOW_SKILL_NAMES = (
|
||||
"gitea-workflow",
|
||||
"llm-project-workflow",
|
||||
"git-pr-workflows",
|
||||
)
|
||||
|
||||
# Primary checked-in skill path (portable source of truth).
|
||||
REPO_SKILL_REL_PATH = "skills/llm-project-workflow/SKILL.md"
|
||||
# In-repo alias directory so scanners that look for skills/gitea-workflow work.
|
||||
REPO_ALIAS_SKILL_REL_PATH = "skills/gitea-workflow/SKILL.md"
|
||||
|
||||
CODEX_SKILLS_ENV = "CODEX_HOME"
|
||||
DEFAULT_CODEX_SKILLS = os.path.expanduser("~/.codex/skills")
|
||||
|
||||
|
||||
def default_codex_skills_dir() -> str:
|
||||
home = (os.environ.get(CODEX_SKILLS_ENV) or "").strip()
|
||||
if home:
|
||||
return os.path.join(os.path.expanduser(home), "skills")
|
||||
return DEFAULT_CODEX_SKILLS
|
||||
|
||||
|
||||
def resolve_repo_skill_path(project_root: str) -> Path:
|
||||
return Path(project_root) / REPO_SKILL_REL_PATH
|
||||
|
||||
|
||||
def resolve_repo_alias_skill_path(project_root: str) -> Path:
|
||||
return Path(project_root) / REPO_ALIAS_SKILL_REL_PATH
|
||||
|
||||
|
||||
def normalize_skill_name(name: str | None) -> str:
|
||||
return (name or "").strip().lower().replace("_", "-")
|
||||
|
||||
|
||||
def is_canonical_workflow_skill_name(name: str | None) -> bool:
|
||||
return normalize_skill_name(name) in CANONICAL_WORKFLOW_SKILL_NAMES
|
||||
|
||||
|
||||
def assess_workflow_skill_presence(
|
||||
project_root: str,
|
||||
*,
|
||||
codex_skills_dir: str | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""Return presence proof for the canonical workflow skill.
|
||||
|
||||
Fail-closed when the in-repo skill file is missing. Codex mount is advisory
|
||||
in the report (operators install via script) but ``codex_skill_mounted`` is
|
||||
explicit so preflight can BLOCK when required.
|
||||
"""
|
||||
root = (project_root or "").strip()
|
||||
reasons: list[str] = []
|
||||
repo_path = resolve_repo_skill_path(root) if root else None
|
||||
alias_path = resolve_repo_alias_skill_path(root) if root else None
|
||||
repo_present = bool(repo_path and repo_path.is_file())
|
||||
alias_present = bool(alias_path and alias_path.is_file())
|
||||
|
||||
if not root:
|
||||
reasons.append("project_root not provided (fail closed)")
|
||||
elif not repo_present:
|
||||
reasons.append(
|
||||
f"canonical workflow skill missing at {REPO_SKILL_REL_PATH} "
|
||||
"(fail closed — do not mutate without workflow wall)"
|
||||
)
|
||||
|
||||
codex_dir = Path(codex_skills_dir or default_codex_skills_dir())
|
||||
codex_mounts: dict[str, bool] = {}
|
||||
for name in CANONICAL_WORKFLOW_SKILL_NAMES:
|
||||
skill_md = codex_dir / name / "SKILL.md"
|
||||
codex_mounts[name] = skill_md.is_file()
|
||||
codex_any = any(codex_mounts.values())
|
||||
|
||||
blocked = bool(reasons)
|
||||
return {
|
||||
"canonical_names": list(CANONICAL_WORKFLOW_SKILL_NAMES),
|
||||
"primary_name": "gitea-workflow",
|
||||
"portable_name": "llm-project-workflow",
|
||||
"repo_skill_rel_path": REPO_SKILL_REL_PATH,
|
||||
"repo_skill_present": repo_present,
|
||||
"repo_alias_rel_path": REPO_ALIAS_SKILL_REL_PATH,
|
||||
"repo_alias_present": alias_present,
|
||||
"codex_skills_dir": str(codex_dir),
|
||||
"codex_skill_mounts": codex_mounts,
|
||||
"codex_skill_mounted": codex_any,
|
||||
"workflow_skill_ready": repo_present,
|
||||
"blocked": blocked,
|
||||
"block_reason": reasons[0] if reasons else None,
|
||||
"reasons": reasons,
|
||||
"safe_next_action": (
|
||||
"Install or repair skills/llm-project-workflow/SKILL.md in the "
|
||||
"repo; run scripts/install-codex-workflow-skill.sh for Codex; "
|
||||
"call mcp_list_project_skills and load gitea-workflow / "
|
||||
"llm-project-workflow before any git/Gitea mutation."
|
||||
if blocked or not codex_any
|
||||
else "Load gitea-workflow or llm-project-workflow, then proceed."
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def workflow_skill_registry_entries() -> dict[str, dict[str, Any]]:
|
||||
"""Shared skill metadata for mcp_list_project_skills / guides."""
|
||||
shared_steps = [
|
||||
"Call mcp_list_project_skills and confirm gitea-workflow (or "
|
||||
"llm-project-workflow) is listed.",
|
||||
"Call mcp_check_workflow_skill_preflight and stop if blocked.",
|
||||
"Load skills/llm-project-workflow/SKILL.md (or the gitea-workflow "
|
||||
"alias) and route to the matching workflow file for the task mode.",
|
||||
"Do not perform git or Gitea mutations until the workflow is loaded.",
|
||||
"Codex: ensure ~/.codex/skills/gitea-workflow -> repo skill via "
|
||||
"scripts/install-codex-workflow-skill.sh.",
|
||||
]
|
||||
notes = (
|
||||
f"Canonical names: {', '.join(CANONICAL_WORKFLOW_SKILL_NAMES)}. "
|
||||
f"Portable source: {REPO_SKILL_REL_PATH}. "
|
||||
"Controller prompts may say gitea-workflow; Codex and MCP inventory "
|
||||
"must resolve the same skill."
|
||||
)
|
||||
base = {
|
||||
"description": (
|
||||
"Canonical Gitea/LLM project workflow router: pick task mode, "
|
||||
"load the matching workflow, enforce isolation, emit the final "
|
||||
"report schema."
|
||||
),
|
||||
"when_to_use": (
|
||||
"At the start of every author, review, merge, reconcile, or "
|
||||
"issue-filing session — before any mutation."
|
||||
),
|
||||
"required_operations": ["gitea.read"],
|
||||
"status": "available",
|
||||
"notes": notes,
|
||||
"steps": shared_steps,
|
||||
"repo_path": REPO_SKILL_REL_PATH,
|
||||
"canonical": True,
|
||||
}
|
||||
return {
|
||||
"gitea-workflow": dict(base),
|
||||
"llm-project-workflow": dict(base),
|
||||
"git-pr-workflows": {
|
||||
**dict(base),
|
||||
"description": (
|
||||
"Legacy alias for the canonical Gitea/LLM project workflow "
|
||||
"router (same as gitea-workflow / llm-project-workflow)."
|
||||
),
|
||||
"notes": notes + " Prefer gitea-workflow in new prompts.",
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user