Teach lane-aware skills in agent dispatch (#143)

Co-authored-by: Codex Agent <codex@hermes.local>
Co-committed-by: Codex Agent <codex@hermes.local>
This commit was merged in pull request #143.
This commit is contained in:
2026-04-04 22:47:31 +00:00
committed by Timmy Time
parent 1b5e31663e
commit 5a60075515
2 changed files with 399 additions and 58 deletions

View File

@@ -1,11 +1,12 @@
#!/usr/bin/env bash
# agent-dispatch.sh — Generate a self-contained prompt for any agent
# agent-dispatch.sh — Generate a lane-aware prompt for any agent
#
# Usage: agent-dispatch.sh <agent_name> <issue_num> <repo>
# agent-dispatch.sh manus 42 Timmy_Foundation/the-nexus
# agent-dispatch.sh groq 42 Timmy_Foundation/the-nexus
#
# Outputs a prompt to stdout. Copy-paste into the agent's interface.
# The prompt includes everything: API URLs, token, git commands, PR creation.
# The prompt includes issue context, repo setup, lane coaching, and
# a short review checklist so dispatch itself teaches the right habits.
set -euo pipefail
@@ -13,86 +14,201 @@ AGENT_NAME="${1:?Usage: agent-dispatch.sh <agent> <issue_num> <owner/repo>}"
ISSUE_NUM="${2:?Usage: agent-dispatch.sh <agent> <issue_num> <owner/repo>}"
REPO="${3:?Usage: agent-dispatch.sh <agent> <issue_num> <owner/repo>}"
GITEA_URL="http://143.198.27.163:3000"
TOKEN_FILE="$HOME/.hermes/${AGENT_NAME}_token"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LANES_FILE="${SCRIPT_DIR%/bin}/playbooks/agent-lanes.json"
if [ ! -f "$TOKEN_FILE" ]; then
echo "ERROR: No token found at $TOKEN_FILE" >&2
echo "Create a Gitea user and token for '$AGENT_NAME' first." >&2
resolve_gitea_url() {
if [ -n "${GITEA_URL:-}" ]; then
printf '%s\n' "${GITEA_URL%/}"
return 0
fi
if [ -f "$HOME/.hermes/gitea_api" ]; then
python3 - "$HOME/.hermes/gitea_api" <<'PY'
from pathlib import Path
import sys
raw = Path(sys.argv[1]).read_text().strip().rstrip("/")
print(raw[:-7] if raw.endswith("/api/v1") else raw)
PY
return 0
fi
if [ -f "$HOME/.config/gitea/base-url" ]; then
tr -d '[:space:]' < "$HOME/.config/gitea/base-url"
return 0
fi
echo "ERROR: set GITEA_URL or create ~/.hermes/gitea_api" >&2
return 1
}
GITEA_URL="$(resolve_gitea_url)"
resolve_token_file() {
local agent="$1"
local normalized
normalized="$(printf '%s' "$agent" | tr '[:upper:]' '[:lower:]')"
for candidate in \
"$HOME/.hermes/${agent}_token" \
"$HOME/.hermes/${normalized}_token" \
"$HOME/.config/gitea/${agent}-token" \
"$HOME/.config/gitea/${normalized}-token"; do
if [ -f "$candidate" ]; then
printf '%s\n' "$candidate"
return 0
fi
done
for candidate in \
"$HOME/.config/gitea/timmy-token" \
"$HOME/.hermes/gitea_token_vps" \
"$HOME/.hermes/gitea_token_timmy"; do
if [ -f "$candidate" ]; then
printf '%s\n' "$candidate"
return 0
fi
done
return 1
}
TOKEN_FILE="$(resolve_token_file "$AGENT_NAME" || true)"
if [ -z "${TOKEN_FILE:-}" ]; then
echo "ERROR: No token found for '$AGENT_NAME'." >&2
echo "Expected one of ~/.hermes/<agent>_token or ~/.config/gitea/<agent>-token" >&2
exit 1
fi
GITEA_TOKEN=$(cat "$TOKEN_FILE")
REPO_OWNER=$(echo "$REPO" | cut -d/ -f1)
REPO_NAME=$(echo "$REPO" | cut -d/ -f2)
GITEA_TOKEN="$(cat "$TOKEN_FILE")"
REPO_OWNER="${REPO%%/*}"
REPO_NAME="${REPO##*/}"
BRANCH="${AGENT_NAME}/issue-${ISSUE_NUM}"
# Fetch issue title
ISSUE_TITLE=$(curl -sf -H "Authorization: token $GITEA_TOKEN" \
"${GITEA_URL}/api/v1/repos/${REPO}/issues/${ISSUE_NUM}" 2>/dev/null | \
python3 -c "import sys,json; print(json.loads(sys.stdin.read())['title'])" 2>/dev/null || echo "Issue #${ISSUE_NUM}")
python3 - "$LANES_FILE" "$AGENT_NAME" "$ISSUE_NUM" "$REPO" "$REPO_OWNER" "$REPO_NAME" "$BRANCH" "$GITEA_URL" "$GITEA_TOKEN" "$TOKEN_FILE" <<'PY'
import json
import sys
import textwrap
import urllib.error
import urllib.request
cat <<PROMPT
You are ${AGENT_NAME}, an autonomous code agent working on the ${REPO_NAME} project.
lanes_path, agent, issue_num, repo, repo_owner, repo_name, branch, gitea_url, token, token_file = sys.argv[1:]
YOUR ISSUE: #${ISSUE_NUM} — "${ISSUE_TITLE}"
with open(lanes_path) as f:
lanes = json.load(f)
GITEA API: ${GITEA_URL}/api/v1
GITEA TOKEN: ${GITEA_TOKEN}
REPO: ${REPO_OWNER}/${REPO_NAME}
lane = lanes.get(agent, {
"lane": "bounded work with explicit verification and a clean PR handoff",
"skills_to_practice": ["verification", "scope control", "clear handoff writing"],
"missing_skills": ["escalate instead of guessing when the scope becomes unclear"],
"anti_lane": ["self-directed backlog growth", "unbounded architectural wandering"],
"review_checklist": [
"Did I stay within scope?",
"Did I verify the result?",
"Did I leave a clean PR and issue handoff?"
],
})
== STEP 1: READ THE ISSUE ==
headers = {"Authorization": f"token {token}"}
curl -s -H "Authorization: token ${GITEA_TOKEN}" "${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/issues/${ISSUE_NUM}"
curl -s -H "Authorization: token ${GITEA_TOKEN}" "${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/issues/${ISSUE_NUM}/comments"
def fetch_json(path):
req = urllib.request.Request(f"{gitea_url}/api/v1{path}", headers=headers)
with urllib.request.urlopen(req, timeout=10) as resp:
return json.loads(resp.read().decode())
Read the issue body AND all comments for context and build order constraints.
try:
issue = fetch_json(f"/repos/{repo}/issues/{issue_num}")
comments = fetch_json(f"/repos/{repo}/issues/{issue_num}/comments")
except urllib.error.HTTPError as exc:
raise SystemExit(f"Failed to fetch issue context: {exc}") from exc
== STEP 2: SET UP WORKSPACE ==
body = (issue.get("body") or "").strip()
body = body[:4000] + ("\n...[truncated]" if len(body) > 4000 else "")
recent_comments = comments[-3:]
comment_block = []
for c in recent_comments:
author = c.get("user", {}).get("login", "unknown")
text = (c.get("body") or "").strip().replace("\r", "")
text = text[:600] + ("\n...[truncated]" if len(text) > 600 else "")
comment_block.append(f"- {author}: {text}")
git clone http://${AGENT_NAME}:${GITEA_TOKEN}@143.198.27.163:3000/${REPO_OWNER}/${REPO_NAME}.git /tmp/${AGENT_NAME}-work-${ISSUE_NUM}
cd /tmp/${AGENT_NAME}-work-${ISSUE_NUM}
comment_text = "\n".join(comment_block) if comment_block else "- (no comments yet)"
Check if branch exists (prior attempt): git ls-remote origin ${BRANCH}
If yes: git fetch origin ${BRANCH} && git checkout ${BRANCH}
If no: git checkout -b ${BRANCH}
skills = "\n".join(f"- {item}" for item in lane["skills_to_practice"])
gaps = "\n".join(f"- {item}" for item in lane["missing_skills"])
anti_lane = "\n".join(f"- {item}" for item in lane["anti_lane"])
review = "\n".join(f"- {item}" for item in lane["review_checklist"])
== STEP 3: UNDERSTAND THE PROJECT ==
prompt = f"""You are {agent}, working on {repo_name} for Timmy Foundation.
Read README.md or any contributing guide. Check for tox.ini, Makefile, package.json.
Follow existing code conventions.
YOUR ISSUE: #{issue_num} — "{issue.get('title', f'Issue #{issue_num}')}"
== STEP 4: DO THE WORK ==
REPO: {repo}
GITEA API: {gitea_url}/api/v1
GITEA TOKEN FILE: {token_file}
WORK BRANCH: {branch}
Implement the fix/feature described in the issue. Run tests if the project has them.
LANE:
{lane['lane']}
== STEP 5: COMMIT AND PUSH ==
SKILLS TO PRACTICE ON THIS ASSIGNMENT:
{skills}
git add -A
git commit -m "feat: <description> (#${ISSUE_NUM})
COMMON FAILURE MODE TO AVOID:
{gaps}
Fixes #${ISSUE_NUM}"
git push origin ${BRANCH}
ANTI-LANE:
{anti_lane}
== STEP 6: CREATE PR ==
ISSUE BODY:
{body or "(empty issue body)"}
curl -s -X POST "${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/pulls" \\
-H "Authorization: token ${GITEA_TOKEN}" \\
RECENT COMMENTS:
{comment_text}
WORKFLOW:
1. Read the issue body and recent comments carefully before touching code.
2. Clone the repo into /tmp/{agent}-work-{issue_num}.
3. Check whether {branch} already exists on origin; reuse it if it does.
4. Read the repo docs and follow its own tooling and conventions.
5. Do only the scoped work from the issue. If the task grows, stop and comment instead of freelancing expansion.
6. Run the repo's real verification commands.
7. Open a PR and summarize:
- what changed
- how you verified it
- any remaining risk or follow-up
8. Comment on the issue with the PR link and the same concise summary.
GIT / API SETUP:
export GITEA_URL="{gitea_url}"
export GITEA_TOKEN_FILE="{token_file}"
export GITEA_TOKEN="$(tr -d '[:space:]' < "$GITEA_TOKEN_FILE")"
git config --global http."$GITEA_URL/".extraHeader "Authorization: token $GITEA_TOKEN"
git clone "$GITEA_URL/{repo}.git" /tmp/{agent}-work-{issue_num}
cd /tmp/{agent}-work-{issue_num}
git ls-remote --exit-code origin {branch} >/dev/null 2>&1 && git fetch origin {branch} && git checkout {branch} || git checkout -b {branch}
ISSUE FETCH COMMANDS:
curl -s -H "Authorization: token $GITEA_TOKEN" "{gitea_url}/api/v1/repos/{repo}/issues/{issue_num}"
curl -s -H "Authorization: token $GITEA_TOKEN" "{gitea_url}/api/v1/repos/{repo}/issues/{issue_num}/comments"
PR CREATION TEMPLATE:
curl -s -X POST "{gitea_url}/api/v1/repos/{repo}/pulls" \\
-H "Authorization: token $GITEA_TOKEN" \\
-H "Content-Type: application/json" \\
-d '{"title": "[${AGENT_NAME}] <description> (#${ISSUE_NUM})", "body": "Fixes #${ISSUE_NUM}\n\n<describe changes>", "head": "${BRANCH}", "base": "main"}'
-d '{{"title":"[{agent}] <description> (#{issue_num})","body":"Fixes #{issue_num}\\n\\n## Summary\\n- <change>\\n\\n## Verification\\n- <command/output>\\n\\n## Risks\\n- <if any>","head":"{branch}","base":"main"}}'
== STEP 7: COMMENT ON ISSUE ==
curl -s -X POST "${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/issues/${ISSUE_NUM}/comments" \\
-H "Authorization: token ${GITEA_TOKEN}" \\
ISSUE COMMENT TEMPLATE:
curl -s -X POST "{gitea_url}/api/v1/repos/{repo}/issues/{issue_num}/comments" \\
-H "Authorization: token $GITEA_TOKEN" \\
-H "Content-Type: application/json" \\
-d '{"body": "PR submitted. <summary>"}'
-d '{{"body":"PR submitted.\\n\\nSummary:\\n- <change>\\n\\nVerification:\\n- <command/output>\\n\\nRisks:\\n- <if any>"}}'
== RULES ==
- Read project docs FIRST.
- Use the project's own test/lint tools.
- Respect git hooks. Do not skip them.
- If tests fail twice, STOP and comment on the issue.
- ALWAYS push your work. ALWAYS create a PR. No exceptions.
- Clean up: remove /tmp/${AGENT_NAME}-work-${ISSUE_NUM} when done.
PROMPT
REVIEW CHECKLIST BEFORE YOU PUSH:
{review}
RULES:
- Do not skip hooks with --no-verify.
- Do not silently widen the scope.
- If verification fails twice or the issue is underspecified, stop and comment with what blocked you.
- Always create a PR instead of pushing to main.
- Clean up /tmp/{agent}-work-{issue_num} when done.
"""
print(textwrap.dedent(prompt).strip())
PY

225
playbooks/agent-lanes.json Normal file
View File

@@ -0,0 +1,225 @@
{
"Timmy": {
"lane": "sovereign review, architecture, release judgment, and governing decisions",
"skills_to_practice": [
"final architectural judgment",
"release and rollback discipline",
"repo-boundary decisions",
"approval on sensitive control surfaces"
],
"missing_skills": [
"delegate routine backlog maintenance instead of carrying it personally"
],
"anti_lane": [
"routine backlog grooming",
"mechanical triage that Allegro can handle"
],
"review_checklist": [
"Does this preserve Timmy's sovereignty and repo boundaries?",
"Does this change require explicit local review before merge?",
"Is the proposed work smaller and more reversible than the previous state?"
]
},
"allegro": {
"lane": "tempo-and-dispatch, Gitea bridge, queue hygiene, and operational next-move selection",
"skills_to_practice": [
"triage discipline",
"queue balancing",
"deduplicating issues and PRs",
"clear review handoffs to Timmy"
],
"missing_skills": [
"say no to work that should stay with Timmy or a builder"
],
"anti_lane": [
"owning final architecture",
"modifying product code without explicit approval"
],
"review_checklist": [
"Is this the best next move, not just a possible move?",
"Does this reduce duplicate work or operational drift?",
"Does Timmy need to judge this before execution continues?"
]
},
"perplexity": {
"lane": "research triage, integration evaluation, architecture memos, and open-source scouting",
"skills_to_practice": [
"compressing research into decisions",
"comparing build-vs-borrow options",
"linking recommendations to issue #542 and current doctrine"
],
"missing_skills": [
"avoid generating duplicate backlog without a collapse pass"
],
"anti_lane": [
"shipping broad implementation without a bounded owner",
"opening speculative issue trees without consolidation"
],
"review_checklist": [
"Did I reduce uncertainty enough for a builder to act?",
"Did I consolidate duplicates instead of multiplying them?",
"Did I separate facts, options, and recommendation clearly?"
]
},
"ezra": {
"lane": "archival memory, RCA, onboarding, durable lessons, and operating history",
"skills_to_practice": [
"extracting durable lessons from sessions",
"writing onboarding docs",
"failure analysis and postmortems",
"turning history into doctrine"
],
"missing_skills": [
"avoid acting like the primary shipper when the work needs a builder"
],
"anti_lane": [
"owning implementation-heavy tickets without backup",
"speculative architecture beyond the historical evidence"
],
"review_checklist": [
"What durable lesson should survive this work?",
"Did I link conclusions to evidence from issues, PRs, or runtime behavior?",
"Would a new wizard onboard faster because of this artifact?"
]
},
"KimiClaw": {
"lane": "long-context reading, extraction, and synthesis before implementation",
"skills_to_practice": [
"digesting large issue threads",
"extracting action items from dense context",
"summarizing codebase slices for builders"
],
"missing_skills": [
"handoff crisp conclusions instead of staying in exploratory mode"
],
"anti_lane": [
"critical-path implementation without a bounded scope",
"becoming a second generic architecture persona"
],
"review_checklist": [
"Did I turn long context into a smaller decision surface?",
"Is my handoff specific enough for a builder to act immediately?",
"Did I avoid speculative side quests?"
]
},
"codex-agent": {
"lane": "workflow hardening, cleanup, migration verification, repo-boundary enforcement, and bounded implementation",
"skills_to_practice": [
"closing migration drift",
"cutting dead code safely",
"packaging changes as reviewable PRs"
],
"missing_skills": [
"stay out of wide ideation unless explicitly asked"
],
"anti_lane": [
"unbounded speculative architecture",
"owning social authority instead of shipping truth"
],
"review_checklist": [
"Did I verify live truth, not just repo intent?",
"Is the change smaller, cleaner, and more reversible?",
"Did I leave a reviewable trail for Timmy and Allegro?"
]
},
"groq": {
"lane": "fast bounded implementation, tactical bug fixes, and narrow feature slices",
"skills_to_practice": [
"keeping changes small",
"shipping with verification",
"staying within the acceptance criteria"
],
"missing_skills": [
"do not trade correctness for speed when the issue is ambiguous"
],
"anti_lane": [
"broad architectural design",
"open-ended exploratory research"
],
"review_checklist": [
"Is the task tightly scoped enough to finish cleanly?",
"Did I verify the fix, not just write it?",
"Did I avoid widening the blast radius?"
]
},
"manus": {
"lane": "moderate-scope support implementation and dependable follow-through on already-scoped work",
"skills_to_practice": [
"finishing bounded tasks cleanly",
"good implementation hygiene",
"clear PR summaries"
],
"missing_skills": [
"escalate when the scope stops being moderate"
],
"anti_lane": [
"owning ambiguous architecture",
"soloing sprawling multi-repo initiatives"
],
"review_checklist": [
"Is this still moderate scope?",
"Did I prove the work and summarize it clearly?",
"Should a higher-context wizard review before more expansion?"
]
},
"claude": {
"lane": "hard refactors, deep implementation, and test-heavy multi-file changes after tight scoping",
"skills_to_practice": [
"respecting scope constraints",
"deep code transformation with tests",
"explaining risks clearly in PRs"
],
"missing_skills": [
"do not let large capability turn into unsupervised backlog or code sprawl"
],
"anti_lane": [
"self-directed issue farming",
"taking broad architecture liberty without a clear charter"
],
"review_checklist": [
"Did I stay inside the scoped problem?",
"Did I leave tests or verification stronger than before?",
"Is there hidden blast radius that Timmy should see explicitly?"
]
},
"gemini": {
"lane": "frontier architecture, research-heavy prototypes, and long-range design thinking",
"skills_to_practice": [
"turning speculation into decision frameworks",
"prototype design under doctrine constraints",
"making architecture legible to builders"
],
"missing_skills": [
"collapse duplicate ideation before it becomes backlog noise"
],
"anti_lane": [
"unsupervised backlog flood",
"acting like a general execution engine for every task"
],
"review_checklist": [
"Is this recommendation strategically important enough to keep?",
"Did I compress, not expand, the decision tree?",
"Did I hand off something a builder can actually execute?"
]
},
"grok": {
"lane": "adversarial review, edge cases, and provocative alternate angles",
"skills_to_practice": [
"finding weird failure modes",
"challenging assumptions safely",
"stress-testing plans"
],
"missing_skills": [
"flag whether a provocative idea is a test, a recommendation, or a risk"
],
"anti_lane": [
"primary ownership of stable delivery",
"final architectural authority"
],
"review_checklist": [
"What assumption fails under pressure?",
"Is this edge case real enough to matter now?",
"Did I make the risk actionable instead of just surprising?"
]
}
}