x-internal-ensure-epic-branch
Single source of truth for the `epic/<ID>` branch convention (RULE-001). Invoked by every epic entry-point (x-epic-create, x-epic-decompose, x-orchestrate-epic, x-implement-epic, x-epic-map) at step 0; ensures the branch exists idempotently both locally and on origin. When the branch is absent, delegates creation to `x-create-git-branch`; when present locally but not on origin, emits a complementary `git push`. Seventh skill in the x-internal-* convention and the first under internal/git/ (after x-internal-update-status / x-internal-write-report / x-internal-normalize-args at internal/ops/ and x-internal-load-story-context / x-internal-build-story-plan / x-internal-verify-story at internal/plan/).
- License 许可
- MIT
- 最近更新
- 2026-10-27
- Stars
- ★ 0
- Forks
- 1
01 前言 Front Matter
- name
- x-internal-ensure-epic-branch
- category
- internal-git
- visibility
- internal
- description
- Single source of truth for the `epic/<ID>` branch convention (RULE-001). Invoked by every epic entry-point (x-epic-create, x-epic-decompose, x-orchestrate-epic, x-implement-epic, x-epic-map) at step 0; ensures the branch exists idempotently both locally and on origin. When the branch is absent, delegates creation to `x-create-git-branch`; when present locally but not on origin, emits a complementary `git push`. Seventh skill in the x-internal-* convention and the first under internal/git/ (after x-internal-update-status / x-internal-write-report / x-internal-normalize-args at internal/ops/ and x-internal-load-story-context / x-internal-build-story-plan / x-internal-verify-story at internal/plan/).
- allowed-tools
- Bash
- argument-hint
- --epic-id <XXXX> [--base <branch>] [--push <true|false>]
- context-budget
- medium
- user-invocable
- false
- requires-capabilities
- []
03 SKILL.md 正文
Global Output Policy
- Language: English ONLY.
- Tone: Technical, Direct, and Concise.
- Efficiency: Remove all conversational fillers and greetings to save tokens.
🔒 INTERNAL SKILL Esta skill é invocada apenas por outras skills (orquestradores). NÃO é destinada a invocação direta pelo usuário. Callers principais: x-epic-create, x-epic-decompose, x-orchestrate-epic, x-implement-epic, x-epic-map (story-0049-0018, 0049-0021, 0049-0022). Sétima skill da convenção
x-internal-*(após x-internal-update-status pilot 0049-0005, x-internal-write-report 0049-0006, x-internal-normalize-args 0049-0007, x-internal-load-story-context 0049-0011, x-internal-build-story-plan 0049-0012, x-internal-verify-story 0049-0014). Primeira skill na subdirinternal/git/— o subdirgit/agrupa operações git de orquestração (ensure de branches de épico; futuras skills de ensure de release branch, merge-back de hotfix, etc.).
Skill: x-internal-ensure-epic-branch
Purpose
Provide a single, idempotent entry point for the RULE-001 convention
"one branch per epic (epic/<ID>), always exists, always pushed". The
orchestrator skills that open an epic lifecycle step MUST call this
skill at step 0 rather than issuing inline git checkout -b or
git push -u origin commands; this eliminates three historical
regressions:
- Duplicate branches created in parallel invocations (two entry-points
launched in the same wave both running
git checkout -b). - Local-only branches that lagged origin for hours because the caller forgot to push (documented in EPIC-0033 post-mortem).
- Divergent base commits between local and remote due to mixed
--baseflags across entry-points.
Responsibilities (single):
- Validate
--epic-idagainst the canonical 4-digit regex. - Resolve branch name →
epic/<ID>. - Detect local existence (
git rev-parse --verify). - Detect remote existence (
git ls-remote origin). - Apply the 3-state decision:
- new (neither) ⇒ delegate to
Skill x-create-git-branch --push; - local-only ⇒ emit complementary
git push -u origin; - local+remote ⇒ idempotent no-op.
- new (neither) ⇒ delegate to
- Emit a single-line JSON envelope consumed by the caller.
This skill does NOT create branches directly — all branch creation
goes through x-create-git-branch (story-0049-0001) to preserve the "one
creation path" invariant of RULE-001.
Convention Anchors (x-internal-* 7th skill)
| Aspect | Value | Rationale |
|---|---|---|
| Path | internal/git/x-internal-ensure-epic-branch/ |
internal/ prefix scopes visibility; git/ groups orchestration-level git ensure operations (distinct from internal/ops/ state mutation and internal/plan/ planning-phase carve-outs). |
Frontmatter visibility |
internal |
Generator filters from /help menu. |
Frontmatter user-invocable |
false |
Declarative complement to visibility: internal. |
| Body marker | > 🔒 **INTERNAL SKILL** as first non-frontmatter content |
Human-visible when browsing the repo. |
| Allowed tools | Bash only |
Minimal surface; delegates to x-create-git-branch via Skill tool when needed. |
| Naming | x-internal-{subject}-{action} |
subject = epic-branch; action = ensure. Mirrors Rule 04 skill taxonomy. |
Audit rule: Rule 22 (Lifecycle Integrity) validates every skill under
internal/** satisfies all 6 anchors above. Violations fail the
LifecycleIntegrityAuditTest.
Triggers
Bare-slash form is intentionally omitted — this skill is never invoked
by a human typing /x-internal-ensure-epic-branch in chat. All
invocations follow Rule 13 INLINE-SKILL pattern from a calling
orchestrator:
Skill(skill: "x-internal-ensure-epic-branch",
args: "--epic-id 0049")
Skill(skill: "x-internal-ensure-epic-branch",
args: "--epic-id 0050 --base main --push true")
Parameters
| Parameter | Required | Default | Description |
|---|---|---|---|
--epic-id <XXXX> |
M | — | 4-digit numeric epic identifier. Must match ^\d{4}$. |
--base <branch> |
O | develop |
Base branch for the delegated creation. Must exist locally. |
--push <true|false> |
O | true |
When true, guarantees the branch is present on origin. When false, creation is local-only and the remote-check step is skipped. |
Response Contract
Successful invocations emit a single-line JSON object on stdout:
| Field | Type | Always Present | Description |
|---|---|---|---|
branchName |
String |
yes | Resolved branch name (epic/<ID>). |
baseSha |
String(40) |
yes | SHA of --base at the moment the skill ran (captured for audit). |
created |
Boolean |
yes | true when the branch was created in this invocation (delegation to x-create-git-branch succeeded). |
alreadyExisted |
Boolean |
yes | true when the branch already existed locally before this invocation. |
pushedNow |
Boolean |
yes | true when this invocation issued the git push -u origin <branch> (either through x-create-git-branch --push or the complementary-push branch). |
Invariant: exactly one of (created, alreadyExisted) is true.
pushedNow is independent; it can be true with either.
Exit Codes
| Code | Name | Condition | Message Format |
|---|---|---|---|
| 0 | SUCCESS | Ensure completed (created, no-op, or complementary push) | — |
| 1 | INVALID_EPIC_ID | --epic-id missing or fails ^\d{4}$ |
Epic ID must be 4 digits |
| 2 | BASE_NOT_FOUND | --base does not resolve locally |
Base branch '<name>' not found |
| 3 | DELEGATION_FAILED | x-create-git-branch returned non-zero |
x-create-git-branch failed with exit <code>: <stderr> |
| 4 | PUSH_FAILED | Complementary git push returned non-zero |
Push to origin failed: <stderr> |
Exit codes 1 and 2 mirror the story's Section 5.3 error-code table. Codes 3 and 4 surface child-process failures without swallowing stderr.
Workflow
Step 1 — Argument parsing and --epic-id validation
EPIC_ID=""
BASE_BRANCH="develop"
PUSH="true"
while [ $# -gt 0 ]; do
case "$1" in
--epic-id) EPIC_ID="$2"; shift 2 ;;
--base) BASE_BRANCH="$2"; shift 2 ;;
--push) PUSH="$2"; shift 2 ;;
*) echo "ERROR: unknown flag: $1" >&2; exit 64 ;;
esac
done
if ! printf '%s' "$EPIC_ID" | grep -Eq '^[0-9]{4}$'; then
echo "INVALID_EPIC_ID — Epic ID must be 4 digits" >&2
exit 1
fi
The 4-digit regex matches the story's Section 5.1 validation rule. The
skill intentionally does NOT accept a feat/ / epic/ prefix in
--epic-id — the caller supplies the raw number; prefix composition
is the skill's responsibility.
Step 2 — Base branch existence check
if ! git rev-parse --verify --quiet "$BASE_BRANCH" >/dev/null; then
echo "BASE_NOT_FOUND — Base branch '$BASE_BRANCH' not found" >&2
exit 2
fi
BASE_SHA=$(git rev-parse --verify "$BASE_BRANCH")
Exits early before touching any network operation. Matches
x-create-git-branch Step 3 semantics.
Step 3 — Compute target branch name
BRANCH_NAME="epic/${EPIC_ID}"
Hard-coded prefix — the RULE-001 convention has exactly one form. No configurability is intentional; variance breaks the "one branch per epic" audit trail.
Step 4 — Local existence detection
ALREADY_EXISTED="false"
if git rev-parse --verify --quiet "$BRANCH_NAME" >/dev/null; then
ALREADY_EXISTED="true"
fi
A local branch is authoritative over remote — the epic lifecycle runs
N times on a developer workstation between pushes. The skill trusts
the local state and only reconciles to remote when --push=true.
Step 5 — Remote existence detection (skipped when --push=false)
REMOTE_EXISTS="false"
if [ "$PUSH" = "true" ]; then
if git ls-remote --exit-code --heads origin "$BRANCH_NAME" >/dev/null 2>&1; then
REMOTE_EXISTS="true"
fi
fi
When --push=false, the remote check is skipped and the
complementary-push branch becomes unreachable. The skill still emits
pushedNow=false in that case; the caller owns the downstream
decision (e.g., a dry-run mode may want local-only).
Step 6 — Three-state decision and execution
CREATED="false"
PUSHED_NOW="false"
if [ "$ALREADY_EXISTED" = "true" ] && [ "$REMOTE_EXISTS" = "true" ]; then
# State A — idempotent no-op
:
elif [ "$ALREADY_EXISTED" = "true" ] && [ "$REMOTE_EXISTS" = "false" ] && [ "$PUSH" = "true" ]; then
# State B — complementary push
if ! git push -u origin "$BRANCH_NAME" 2>/tmp/epic-branch-ensure.push.err; then
echo "PUSH_FAILED — Push to origin failed" >&2
cat /tmp/epic-branch-ensure.push.err >&2
exit 4
fi
PUSHED_NOW="true"
elif [ "$ALREADY_EXISTED" = "false" ]; then
# State C — delegate creation to x-create-git-branch
#
# Invocation shape (Rule 13 INLINE-SKILL):
#
# Skill(skill: "x-create-git-branch",
# args: "--name epic/<ID> --base <BASE_BRANCH> [--push]")
#
# The calling orchestrator forwards --push=true/false verbatim. A
# successful invocation populates baseSha, created=true, pushed=<push>
# on the x-create-git-branch response; this skill re-exports those as
# created=true, pushedNow=<push>.
echo "DELEGATE_TO_X_GIT_BRANCH — name=$BRANCH_NAME base=$BASE_BRANCH push=$PUSH" >&2
# The orchestrator layer performs the actual Skill(...) call and
# captures the result; see the full protocol for the end-to-end
# envelope translation.
CREATED="true"
if [ "$PUSH" = "true" ]; then
PUSHED_NOW="true"
fi
fi
State A, B, and C are mutually exclusive. The caller can distinguish
them post-hoc by inspecting (created, alreadyExisted, pushedNow).
Step 7 — Emit structured response
printf '{"branchName":"%s","baseSha":"%s","created":%s,"alreadyExisted":%s,"pushedNow":%s}\n' \
"$BRANCH_NAME" "$BASE_SHA" "$CREATED" "$ALREADY_EXISTED" "$PUSHED_NOW"
Single-line JSON keeps parsing cost at O(1) for the caller, which is typically the very first step of an epic entry-point — any overhead here is paid N times per day on a developer workstation.
Examples
Example 1 — New epic, branch absent locally and remotely
Skill(skill: "x-internal-ensure-epic-branch",
args: "--epic-id 0050")
Expected stdout:
{"branchName":"epic/0050","baseSha":"<sha>","created":true,"alreadyExisted":false,"pushedNow":true}
Exit: 0. Matches Section 7 Gherkin scenario "Criar branch nova quando epic é novo".
Example 2 — Idempotent no-op, branch present locally + remotely
Skill(skill: "x-internal-ensure-epic-branch",
args: "--epic-id 0049")
Expected stdout:
{"branchName":"epic/0049","baseSha":"<sha>","created":false,"alreadyExisted":true,"pushedNow":false}
Exit: 0. Matches "Idempotência — branch já existe local + remoto".
Example 3 — Complementary push, local-only branch
# Branch created manually earlier; never pushed.
Skill(skill: "x-internal-ensure-epic-branch",
args: "--epic-id 0049 --push true")
Expected stdout:
{"branchName":"epic/0049","baseSha":"<sha>","created":false,"alreadyExisted":true,"pushedNow":true}
Exit: 0. Matches "Push complementar — branch local mas não remota".
Example 4 — Custom base
Skill(skill: "x-internal-ensure-epic-branch",
args: "--epic-id 0051 --base main")
Expected stdout:
{"branchName":"epic/0051","baseSha":"<sha>","created":true,"alreadyExisted":false,"pushedNow":true}
Exit: 0.
Example 5 — Missing base branch
Skill(skill: "x-internal-ensure-epic-branch",
args: "--epic-id 0052 --base mybase")
Expected stderr:
BASE_NOT_FOUND — Base branch 'mybase' not found
Exit: 2. Matches "Erro — base inexistente".
Example 6 — Malformed epic-id
Skill(skill: "x-internal-ensure-epic-branch",
args: "--epic-id 49")
Expected stderr:
INVALID_EPIC_ID — Epic ID must be 4 digits
Exit: 1. Matches "Boundary — epic-id mal formado".
Example 7 — Local-only flow (--push false)
Skill(skill: "x-internal-ensure-epic-branch",
args: "--epic-id 0053 --push false")
Expected stdout:
{"branchName":"epic/0053","baseSha":"<sha>","created":true,"alreadyExisted":false,"pushedNow":false}
Exit: 0. The skill never issues a network operation; suitable for offline dry-runs.
Error Handling
| Scenario | Action |
|---|---|
--epic-id missing or fails regex |
Exit 1 (INVALID_EPIC_ID); stderr carries the offending value. |
--base absent locally |
Exit 2 (BASE_NOT_FOUND); suggest git fetch origin. |
x-create-git-branch delegation fails (State C) |
Exit 3 (DELEGATION_FAILED); stderr carries x-create-git-branch stderr verbatim. |
git push fails in State B |
Exit 4 (PUSH_FAILED); stderr carries git push stderr verbatim. |
git ls-remote times out or auth fails |
Exit 4 (PUSH_FAILED); remote-check failure is treated as a push-side failure. |
| Unknown flag | Exit 64 (sysexits EX_USAGE); print usage: banner. |
git absent on PATH |
Exit 127 with git is required; abort before any state mutation. |
All exits preserve set -e / trap-free semantics: no temp files are
written in the happy path, and the complementary-push temp file at
/tmp/epic-branch-ensure.push.err is overwritten on the next
invocation (no accumulation).
Idempotency Contract
The skill satisfies RULE-002 idempotency over the full 3-state matrix:
| State before | First call | Second call |
|---|---|---|
| Branch absent (local + remote) | created=true, alreadyExisted=false, pushedNow=true |
created=false, alreadyExisted=true, pushedNow=false |
| Branch local, not remote | created=false, alreadyExisted=true, pushedNow=true |
created=false, alreadyExisted=true, pushedNow=false |
| Branch local + remote | created=false, alreadyExisted=true, pushedNow=false |
created=false, alreadyExisted=true, pushedNow=false |
Two concurrent invocations of the same --epic-id serialize on the
git process lock; the later caller always observes the earlier
caller's result as "already exists". No flock is required — git's
own .git/index.lock provides the critical-section guarantee at the
granularity we need.
Performance Contract (story Section 3.5)
| Operation | Budget |
|---|---|
| State A (idempotent no-op; no network) | < 1s (two git rev-parse calls + ls-remote) |
| State B (complementary push) | < 3s (ls-remote + single push round-trip) |
State C (delegate to x-create-git-branch) |
< 3s (branch create + push; matches x-create-git-branch budget) |
The "< 1s" ceiling for idempotency is the dominant case in a
multi-entry-point workflow: x-implement-epic followed by
x-orchestrate-epic followed by x-epic-map on the same epic will
trigger State A three times; the aggregate overhead must stay sub-3s.
Testing
The story ships the following acceptance test scenarios, which are the reference contract every downstream caller (0049-0018, 0049-0021, 0049-0022) can rely on:
- Happy path (State C) — branch absent both sides; assert
created=true, alreadyExisted=false, pushedNow=true; assert the ref exists locally (git rev-parse) and on origin (git ls-remote). - Idempotency (State A) — second invocation of the same epic ID
yields
created=false, alreadyExisted=true, pushedNow=false; file tree unchanged;.git/packed-refsunchanged. - Complementary push (State B) — pre-create local branch without
pushing, then invoke skill; assert
pushedNow=true; origin ref now present. --push false— assert no network call;pushedNow=falseregardless of remote state.- BASE_NOT_FOUND — pass
--base unknown; exit 2; message containsBASE_NOT_FOUND. - INVALID_EPIC_ID — pass
--epic-id 49; exit 1; message containsINVALID_EPIC_ID.
No Java production code is introduced by this story; the SKILL.md is
the deliverable. Golden-file smoke tests are covered upstream by the
generator's existing skill-inventory goldens — this skill, being
visibility: internal, does NOT appear in the /help menu or in
.claude/README.md, so no golden-file updates are required.
Generator Filter Contract
The ia-dev-env generator MUST exclude skills with
visibility: internal from:
- The
.claude/README.mdskill-inventory table. - The
/helpmenu listing surfaced by Claude Code. - User-facing autocomplete in the chat input.
Internal skills ARE still copied into .claude/skills/ (flat layout)
so Skill(skill: "x-internal-ensure-epic-branch") invocations from
other skills resolve correctly. The invariant: user cannot see
them; orchestrators can invoke them.
Telemetry
Internal skills DO NOT emit phase.start / phase.end markers —
telemetry is produced by the invoking orchestrator (the phase
wrapping the orchestrator's own step 0 is the correct aggregation
boundary). Passive hooks still capture tool.call for the underlying
Bash invocation.
Reference: Rule 13 (Skill Invocation Protocol), Rule 22 (Lifecycle Integrity Audit), ADR-0010 (Interactive Gates Convention — exempts internal skills from the 3-option menu contract).
Rule References
- RULE-001 (EPIC-0049) — Branch única por épico (
epic/<ID>). This skill IS the canonical enforcement point. - RULE-005 (EPIC-0049) — Thin orchestrator (UseCase pattern). Epic entry-points call this skill instead of inlining branch logic.
- RULE-006 (EPIC-0049) —
x-internal-*convention. This is the seventh skill to follow the convention. - Rule 09 (Branching Model) —
epic/<ID>is a canonical prefix in the generic branch-name regex. - Rule 13 (Skill Invocation Protocol) — INLINE-SKILL pattern used by all callers.
- Rule 22 (Lifecycle Integrity Audit) — validates the 6 convention anchors above.
Integration Notes
| Skill | Relationship | Context |
|---|---|---|
x-create-git-branch |
delegate (State C) | The only skill this one calls. Handles actual git branch + optional push. |
x-epic-create |
caller (future — story-0049-0018) | Step 0 before writing epic metadata. |
x-epic-decompose |
caller (future — story-0049-0018) | Step 0 before writing stories / IMPLEMENTATION-MAP. |
x-orchestrate-epic |
caller (future — story-0049-0021) | Step 0 before per-story planning loop. |
x-implement-epic |
caller (future — story-0049-0021) | Step 0 before Phase 0 preparation. |
x-epic-map |
caller (future — story-0049-0022) | Step 0 before IMPLEMENTATION-MAP refresh. |
x-internal-update-status |
peer (internal/ops/) | Independent — mutates execution-state; this skill mutates git refs. Never co-serialized. |
x-internal-normalize-args |
peer (internal/ops/) | Future refactor may adopt it for flag parsing; current implementation uses inline Bash parsing for minimal surface. |
Downstream stories that depend on this skill: story-0049-0018 (epic-create / epic-decompose refactor), story-0049-0021 (epic-orchestrate / epic-implement refactor), story-0049-0022 (epic-map refactor).
04 同仓库更多 Skills
x-parallel-eval
Detects and classifies file-collision risks between work units (tasks, stories, or epic phases) before parallel execution. Reads ## File Footprint blocks produced by x-task-plan / x-story-plan, applies the parallelism-heuristics knowledge pack (hard / regen / soft categories + hotspot overrides), and emits a collision matrix + serialization recommendation in Markdown (default) or JSON.
parallelism-heuristics
Canonical catalog of file-collision heuristics for parallel task, story, and epic execution. Defines the File Footprint block format, the three Conflict Categories (hard / regen / soft), a hotspot list of high-contention paths, and the degrade-with-warning policy. Consumed by x-task-plan, x-story-plan, x-epic-map, x-parallel-eval, and x-dev-*-implement.
x-watch-pr-ci
Polls a PR's CI checks and Copilot review status, blocking until checks complete or timeout. Returns one of 8 stable exit codes (SUCCESS=0, CI_PENDING_PROCEED=10, CI_FAILED=20, TIMEOUT=30, PR_ALREADY_MERGED=40, NO_CI_CONFIGURED=50, PR_CLOSED=60, PR_NOT_FOUND=70). Writes a versioned state-file for session resume.
x-validate-docs
Documentation freshness gate: validates 6 dimensions (readme, api-specs, grpc-proto, adr, skill-docs, system-architecture) against code changes in a PR. Stack-aware — only validates targets declared/auto-detected from ProjectConfig.documentation.targets. Returns exit non-zero on staleness; produces structured report.
x-update-system-architecture
Incrementally updates docs/architecture/system.md after an epic completes. Appends new entries to the Decision Log (§11) using x-internal-write-report --append (dedup by ## ID: marker) and surgically inserts component/integration changes into sections 1-10 via Edit. Idempotent: re-running with the same epic produces a byte-identical system.md.
x-update-architecture
Incrementally updates the service architecture document with changes from architecture plans. Adds new components, integrations, flows, and ADR references without rewriting existing content. Use after implementation to keep architecture documentation current.