# Ticket Work Orchestration

You are executing contract-driven ticket work. This prompt defines the complete orchestration logic.

## Autonomous Mode

If `--autonomous` was passed, gate behavior changes across all phases. Before running any phase
handler, check whether autonomous mode is active. In autonomous mode:

- Hard keyboard gates are replaced by Slack soft-gates or auto-advance (see SKILL.md for the
  full gate behavior table).
- The phase handlers below describe **interactive mode** (default) gate behavior.
- For each phase exit, substitute the autonomous-mode behavior from SKILL.md when `--autonomous`
  is active.

## Initialization

When `/ticket-work {ticket_id} [--autonomous]` is invoked:

1. **Fetch the ticket:**
   ```
   mcp__linear-server__get_issue(id="{ticket_id}")
   ```

2. **Parse the contract** from the ticket description:
   - Look for `## Contract` section followed by a YAML code block
   - If found: parse and validate the YAML
   - If not found: create initial contract (phase: intake)

3. **Announce current state:**
   ```
   Resuming {ticket_id} in {phase} phase.

   Pending items:
   - {list pending questions if any}
   - {list blocking verification if any}
   - {list pending gates if any}
   ```

4. **Guide based on current phase** (see Phase Handlers below)

---

## Contract Schema

```yaml
# Identity (set at intake)
ticket_id: "OMN-1234"
title: "Feature title"
repo: "omnibase_core"
branch: null  # set during implementation

# State
phase: intake  # intake|research|questions|spec|implementation|review|done
created_at: "2026-02-01T12:00:00Z"
updated_at: "2026-02-01T12:00:00Z"

# Research context (populated during research phase)
context:
  relevant_files: []
  patterns_found: []
  notes: ""

# Clarification (populated during questions phase)
questions:
  - id: "q1"
    text: "What authentication method should we use?"
    category: "architecture"  # architecture|behavior|integration|scope
    required: true
    answer: null
    answered_at: null

# Specification (populated during spec phase)
requirements:
  - id: "r1"
    statement: "System shall authenticate users via OAuth2"
    rationale: "Matches existing auth patterns"
    acceptance:
      - "OAuth2 flow implemented"
      - "Token refresh works"

verification:
  - id: "v1"
    title: "Unit tests pass"
    kind: "unit_tests"  # unit_tests|lint|mypy|integration|manual_check|script
    command: "uv run pytest tests/"
    expected: "exit 0"
    blocking: true
    status: "pending"  # pending|passed|failed|skipped
    evidence: null
    executed_at: null

gates:
  - id: "g1"
    title: "Human approval"
    kind: "human_approval"  # human_approval|policy_check|security_check
    required: true
    status: "pending"  # pending|approved|rejected|skipped
    notes: null
    resolved_at: null

# Completion tracking
commits: []
pr_url: null
hardening_tickets: []
```

---

## Phase Handlers

### Phase: intake

**Entry:** Always allowed (initial phase)

**Actions:**
1. Create contract if not exists
2. Set identity fields:
   - `ticket_id`: From Linear ticket identifier
   - `title`: From Linear ticket title
   - `repo`: From current working directory basename (e.g., `/Volumes/PRO-G40/Code/omni_worktrees/OMN-2068/omniclaude` → `omniclaude`)  # local-path-ok
3. Save contract to ticket description
4. **Auto-advance to research phase** (no human gate needed)

**Mutations allowed:** Create contract only

**Exit to research:** Automatic after contract creation (no confirmation needed)

---

### Phase: research

**Entry invariant:** Contract exists

**Actions:**
1. **Load architecture handshake** (if available):
   - Read `.claude/architecture-handshake.md` using the Read tool
   - If file exists: review repo-specific constraints and patterns
   - If file doesn't exist: continue without (graceful fallback)
   - Include relevant constraints in `context.notes`
2. Analyze the ticket requirements
3. Use codebase exploration to identify:
   - Relevant files (`context.relevant_files`)
   - Existing patterns (`context.patterns_found`)
   - Notes about approach (`context.notes`)
4. **Query code graph for relevant entities** *(graceful degradation)*:

   Run the code graph semantic query with the ticket title as search text.
   Always pass `repos` to scope results to the ticket's target repo:
   ```bash
   source ~/.omnibase/.env
   echo '{"mode": "semantic", "query": "{ticket_title}", "repos": ["{ticket_repo}"], "limit": 20}' \
     | python3 "${OMNICLAUDE_PROJECT_ROOT:-$CLAUDE_PLUGIN_ROOT}/plugins/onex/hooks/lib/code_graph_query.py"
   ```

   If the command succeeds and returns `"status": "ok"`:
   - Take only the **top 5** results by relevance_score (do not dump all 20 into context)
   - For each, append to `context.patterns_found`:
     `"{entity_type} {qualified_name} ({source_repo}) — {classification}"`
   - Append to `context.notes`: "Code graph queried for: {ticket_title} — {N} of {total} entities surfaced (top-5 by relevance)"

   If the command fails or returns `"status": "service_unavailable"`:
   - Skip silently. Do not block research phase.
   - Append to `context.notes`: "Code graph unavailable — skipped entity enrichment"

5. Generate clarifying questions (add to `questions[]`)
5. Save contract after each mutation

**Mutations allowed:**
- `context.relevant_files`
- `context.patterns_found`
- `context.notes`
- `questions[]` (append only)

**Research boundaries:**
- MAY populate context
- MAY generate questions
- MAY NOT add requirements
- MAY NOT add verification steps
- MAY NOT answer questions
- MAY NOT advance phase

**Exit to questions:**
- Invariant: `len(context.relevant_files) >= 1`
- User says "questions ready" or similar

---

### Phase: questions

**Entry invariant:** `len(context.relevant_files) >= 1`

**Actions:**
1. Present unanswered questions one at a time using `AskUserQuestion`
2. Record answers in `questions[].answer` and `questions[].answered_at`
3. Save contract after each answer

**Mutations allowed:**
- `questions[].answer`
- `questions[].answered_at`

**Exit to spec:**
- Invariant: All required questions have non-empty answers
- Human gate: User says "requirements clear", "proceed to spec", or confirms via AskUserQuestion:
  ```
  All questions answered. Ready to proceed to spec phase?
  - Yes, requirements are clear
  - No, I have more questions
  ```

---

### Phase: spec

**Entry invariant:** `is_questions_complete()` AND human signal

**Actions:**
1. Generate requirements based on ticket + context + answers
2. Generate verification steps (default: unit_tests, lint, mypy)
3. Generate gates (default: human_approval)
4. Present spec for review
5. Allow edits via user feedback

**Mutations allowed:**
- `requirements[]` (append; edits allowed during spec phase only)
- `verification[]` (append; edits allowed during spec phase only)
- `gates[]` (append; edits allowed during spec phase only)

**Exit to implementation:**
- Invariant: `len(requirements) >= 1` AND `len(verification) >= 1`
- Human gate: User says "approve spec", "build it", or confirms via AskUserQuestion:
  ```
  Spec complete with {N} requirements and {M} verification steps. Ready to implement?
  - Yes, build it
  - No, needs changes
  ```

**🚨 MANDATORY AUTOMATION on spec→implementation transition:**

**BEFORE ANY IMPLEMENTATION WORK BEGINS**, when user approves spec (says "approve spec", "build it", etc.), you MUST execute these steps IN ORDER:

1. **Create git branch** using Linear's suggested branch name:
   - Get branch name from `mcp__linear-server__get_issue(id="{ticket_id}")` response field `branchName`
   - Linear auto-generates this based on ticket ID and title
   - **Check if branch already exists before creating:**
   ```bash
   # Check if branch exists
   if git show-ref --verify --quiet refs/heads/{branchName}; then
       # Branch exists - checkout existing branch
       git checkout {branchName}
       BRANCH_CREATED=false
   else
       # Branch doesn't exist - create new
       git checkout -b {branchName}
       BRANCH_CREATED=true
   fi
   # Example: git checkout -b jonah/omn-1830-m4-hook-integration-session-continuity

   # MANDATORY: pre-commit hooks are not inherited by worktrees.
   # Run this before any commit; without it hooks are silently skipped.
   pre-commit install
   ```
   Track `BRANCH_CREATED` for rollback safety.

2. **Update Linear status** to "In Progress":
   ```
   mcp__linear-server__update_issue(id="{ticket_id}", state="In Progress")
   ```

   > **Note**: This assumes the workspace uses "In Progress" as the active state name.
   > If the update fails with "state not found", query available states with
   > `mcp__linear-server__list_issue_statuses(team="{team_id}")` and use the appropriate state name.

3. **Update contract** with branch name:
   - Set `branch` field to the git branch name
   - Persist to both Linear and local

4. **Announce readiness and dispatch implementation:**
   ```
   Branch created: {branchName}
   Ticket moved to In Progress
   Ready for implementation

   Dispatching {N} requirements to implementation agent...
   ```

   Then immediately dispatch implementation to a separate agent:

   > **Note**: This dispatch block is intentionally duplicated from the implementation phase handler. Both are needed because the spec-to-implementation transition and the phase handler context are different entry points.

   ```
   Task(
     subagent_type="onex:polymorphic-agent",
     description="Implement {ticket_id}: {title}",
     prompt="Implement the following requirements for {ticket_id}: {title}.

       Requirements:
       {requirements_list}

       Relevant files:
       {relevant_files}

       Execute the implementation. Do NOT commit changes (the orchestrator handles git).
       Report: files changed, what was implemented, any issues encountered."
   )
   ```

**⚠️ DO NOT proceed to implementation actions until ALL automation steps complete successfully.**

**Error handling for automation steps:**

If any step fails, follow this rollback sequence:

| Failure Point | Rollback Action |
|---------------|-----------------|
| Git checkout fails | Stop. Do not update Linear or contract. Report error to user. |
| Linear update fails | If `BRANCH_CREATED=true`, checkout previous branch (`git checkout -`) then delete (`git branch -D {branchName}`). Report error to user. |
| Contract persistence fails | Log warning and continue (Linear is source of truth). |

Each step should check for success before proceeding to the next:
```python
# Pseudo-code for safe automation
try:
    # Step 1: Check for existing branch and create/checkout
    branch_exists = run("git show-ref --verify refs/heads/{branchName}").success
    branch_created = False

    if branch_exists:
        git_result = run("git checkout {branchName}")
    else:
        git_result = run("git checkout -b {branchName}")
        branch_created = True

    if git_result.failed:
        raise AutomationError("Git checkout failed", step=1)

    # Step 2: Update Linear
    try:
        mcp__linear_server__update_issue(id=ticket_id, state="In Progress")
    except Exception as e:
        if branch_created:  # Only delete if we created it
            checkout_result = run("git checkout -")  # Return to previous branch first
            if checkout_result.success:
                run("git branch -D {branchName}")
            else:
                log_warning(f"Could not checkout previous branch. Branch '{branchName}' must be deleted manually.")
        raise AutomationError(f"Linear update failed: {e}", step=2)

    # Step 3: Persist contract locally
    try:
        persist_contract_locally(ticket_id, contract)
    except Exception as e:
        # Don't rollback - Linear is source of truth
        log_warning(f"Local persistence failed: {e}")
except AutomationError as e:
    report_to_user(e)
```

---

### Phase: implementation

**Entry invariant:** `is_spec_complete()` AND human signal AND branch created AND Linear status = "In Progress"

**Pre-conditions (set by spec→implementation automation):**
- Git branch exists and is checked out
- Linear ticket status is "In Progress"
- Contract `branch` field is populated

**Actions:**
1. Verify branch is checked out (should already exist from transition automation)
2. **Mandatory code tracing (OMN-6819):** Before dispatching implementation, the agent
   MUST read and trace the relevant code paths identified during research. This is NOT
   optional. For each file in `context.relevant_files`:
   - Read the file using the Read tool
   - Identify relevant functions, classes, and data flow
   - Note constraints, patterns, or conventions used
   - Record trace findings in the dispatch prompt

   Minimum trace requirement: at least 2 files from `context.relevant_files` must be
   read before dispatching the implementation agent. If `context.relevant_files` has
   fewer than 2 entries, trace all of them.

   **Rationale:** Agents that propose fixes without understanding existing code paths
   cause regressions, break integrations, and waste review cycles. Tracing first
   prevents superficial analysis.
3. **Dispatch implementation to a separate agent:**
   ```
   Task(
     subagent_type="onex:polymorphic-agent",
     description="Implement {ticket_id}: {title}",
     prompt="Implement the following requirements for {ticket_id}: {title}.

       Requirements:
       {requirements_summary}

       Relevant files:
       {context.relevant_files}

       Code trace findings (from mandatory pre-implementation tracing):
       {code_trace_findings}

       The code trace findings above summarize what the orchestrator learned by reading
       the relevant files. Use these findings to understand existing patterns and
       constraints before making changes.

       Execute the implementation. Do NOT commit changes (the orchestrator handles git).
       Report: files changed, what was implemented, any issues encountered."
   )
   ```

   This spawns a polymorphic agent with its own context window to implement the requirements.

4. After the implementation agent completes, commit changes (append to `commits[]`)
5. Update `pr_url` if PR created

**Implementation via Task dispatch:**
- Requirements from the contract are passed to a polymorphic agent via Task()
- The agent runs in its own context window (avoids exhausting the orchestrator's context)
- The orchestrator reads the agent's result to determine success/failure
- Quality gates are enforced after the agent completes

**Mutations allowed:**
- `branch`
- `commits[]` (append only)
- `pr_url`

**Exit to review:**
- Invariant: `len(commits) >= 1`
- Human gate: User says "create PR", "ready for review", or confirms via AskUserQuestion:
  ```
  Implementation complete with {N} commits. Ready for review?
  - Yes, run verification
  - No, more changes needed
  ```

---

### Phase: review

**Entry invariant:** `len(commits) >= 1` AND human signal

**Actions:**

1. **Push branch and create PR:**
   ```bash
   git push -u origin {branch}
   # Use heredoc for PR body to safely handle special characters in title
   gh pr create --title "$(cat <<'EOF'
   {ticket_id}: {title}
   EOF
   )" --body "$(cat <<'EOF'
   ...PR body content...
   EOF
   )"
   ```

   **Shell safety**: The heredoc syntax (`<<'EOF'`) with single-quoted delimiter prevents shell expansion of special characters in ticket titles. This protects against command injection if titles contain backticks, dollar signs, or semicolons.

   Update `pr_url` in contract.

2. **Code review loop** (repeat until done):
   ```
   Review code locally → Find issues → Fix issues → Commit → Re-review
   ```

   - Review all changed files for:
     - Logic errors
     - Missing edge cases
     - Inconsistencies with requirements
     - Documentation accuracy
   - Fix issues found (do NOT push between iterations)
   - Re-review locally
   - Continue until:
     - All issues fixed, OR
     - Only nitpicks/minor issues remain (can defer)

3. **Run verification steps:**
   ```bash
   uv run pytest tests/           # unit_tests
   uv run ruff check .            # lint
   uv run mypy src/               # mypy
   ```

4. **Update contract:**
   - `verification[].status` (passed/failed)
   - `verification[].evidence` (command output)
   - `verification[].executed_at`

5. **Return to user** with review summary:
   - Issues found and fixed
   - Remaining minor/nitpick issues (if any)
   - Verification results
   - Ask for approval to proceed

6. If hardening tickets needed, create and add to `hardening_tickets[]`

**Important:** Do NOT merge the PR. That decision belongs to the user.

**Mutations allowed:**
- `commits[]` (append only - from review fixes)
- `verification[].status`
- `verification[].evidence`
- `verification[].executed_at`
- `gates[].status`
- `gates[].notes`
- `gates[].resolved_at`
- `hardening_tickets[]` (append only)

**Exit to done:**
- Invariant: All blocking verification passed/skipped AND all required gates approved
- Human gate: User says "approve merge", "ship it", or confirms via AskUserQuestion:
  ```
  All verification passed and gates approved. Ready to complete?
  - Yes, ship it
  - No, not yet
  ```

---

### Phase: done

**Entry invariant:** `is_verification_complete()` AND `is_gates_complete()` AND human signal

**Actions:**
1. Mark contract as complete
2. Update Linear ticket status
3. Announce completion

**Mutations allowed:** None (contract is immutable in done phase)

---

## Contract Persistence

### Detecting Repo

```python
def get_current_repo() -> str:
    """Extract repo name from current working directory.

    Example: /Volumes/PRO-G40/Code/omni_worktrees/OMN-2068/omniclaude -> omniclaude  # local-path-ok
    """
    import os
    return os.path.basename(os.getcwd())
```

### Reading Contract

```python
def extract_contract(description: str) -> dict | None:
    """Extract contract YAML from ticket description."""
    marker = "## Contract"
    if marker not in description:
        return None

    # Find last occurrence of ## Contract
    idx = description.rfind(marker)
    contract_section = description[idx:]

    # Extract YAML from fenced code block
    import re
    match = re.search(r'```(?:yaml|YAML)?\s*\n(.*?)\n\s*```', contract_section, re.DOTALL)
    if not match:
        return None

    import yaml
    try:
        return yaml.safe_load(match.group(1))
    except yaml.YAMLError:
        return None
```

### Writing Contract

```python
def update_description_with_contract(description: str, contract: dict) -> str:
    """Update ticket description with contract, preserving original content."""
    import yaml
    import re

    marker = "## Contract"
    contract_yaml = yaml.dump(contract, default_flow_style=False, sort_keys=False)
    contract_block = f"\n---\n{marker}\n\n```yaml\n{contract_yaml}```\n"

    if marker in description:
        # Replace existing contract section
        idx = description.rfind(marker)
        # Find --- on its own line immediately before ## Contract
        delimiter_match = re.search(r'\n---\n\s*$', description[:idx])
        if delimiter_match:
            return description[:delimiter_match.start()] + contract_block
        return description[:idx] + contract_block
    else:
        # Append new contract section
        return description.rstrip() + contract_block
```

### Saving to Linear

After any contract mutation:
```
mcp__linear-server__update_issue(
    id="{ticket_id}",
    description="{updated_description}"
)
```

### Local Persistence

After saving to Linear, also persist locally for hook access:

```python
def persist_contract_locally(ticket_id: str, contract: dict) -> None:
    """Persist contract to local filesystem for hook injection.

    Path: $ONEX_STATE_DIR/tickets/{ticket_id}/contract.yaml
    """
    import yaml
    from pathlib import Path

    tickets_dir = Path.home() / ".claude" / "tickets" / ticket_id
    tickets_dir.mkdir(parents=True, exist_ok=True)

    contract_path = tickets_dir / "contract.yaml"

    # Atomic write
    tmp_path = contract_path.with_suffix('.yaml.tmp')
    tmp_path.write_text(yaml.dump(contract, default_flow_style=False, sort_keys=False))
    tmp_path.rename(contract_path)
```

**Error handling:**

Wrap calls to `persist_contract_locally()` in try-except. Failures should log a warning but not block the workflow (Linear is the source of truth):

```python
try:
    persist_contract_locally(ticket_id, contract)
except PermissionError as e:
    print(f"Warning: Cannot write to tickets directory: {e}")
except OSError as e:
    print(f"Warning: Failed to persist contract locally: {e}")
# Continue workflow - Linear has the authoritative copy
```

Call this after every `mcp__linear-server__update_issue()` that modifies the contract.

---

## Verification Commands (v1 Hardcoded)

```python
VERIFICATION_COMMANDS = {
    "unit_tests": "uv run pytest tests/",
    "lint": "uv run ruff check .",
    "mypy": "uv run mypy src/",
    "integration": "uv run pytest tests/integration/",
}
```

For `manual_check` kind: Present description and ask user to confirm pass/fail.
For `script` kind: Use the `command` field from the verification step.

---

## Error Handling

| Error | Behavior |
|-------|----------|
| Linear MCP failure | Fail closed, explain, do not advance |
| YAML parse failure | Fail closed, show raw content, ask user to fix |
| Contract validation failure | Fail closed, show errors, do not persist |
| Verification crash | Mark `failed`, capture error output, do not advance |
| Invariant violation | Refuse transition, explain which invariant failed |

**Never:**
- Silently swallow errors
- Persist invalid contract state
- Advance phase without explicit human signal (except intake→research which is automatic)

---

## Completion Checks

These are the logic for completion checks (mirrors ModelTicketContract from omnibase_core):

```python
def is_questions_complete(contract) -> bool:
    """All required questions have non-empty answers."""
    return all(
        q.get("answer") and q["answer"].strip()
        for q in contract.get("questions", [])
        if q.get("required", True)
    )

def is_spec_complete(contract) -> bool:
    """At least one requirement with acceptance criteria."""
    reqs = contract.get("requirements", [])
    return len(reqs) > 0 and all(
        len(r.get("acceptance", [])) > 0
        for r in reqs
    )

def is_verification_complete(contract) -> bool:
    """All blocking verification passed or skipped."""
    return all(
        v.get("status") in ("passed", "skipped")
        for v in contract.get("verification", [])
        if v.get("blocking", True)
    )

def is_gates_complete(contract) -> bool:
    """All required gates approved."""
    return all(
        g.get("status") == "approved"
        for g in contract.get("gates", [])
        if g.get("required", True)
    )

def is_done(contract) -> bool:
    """Contract is complete."""
    return (
        contract.get("phase") == "done"
        and is_questions_complete(contract)
        and is_spec_complete(contract)
        and is_verification_complete(contract)
        and is_gates_complete(contract)
    )
```

---

## Mutation Rules

| Rule | Description |
|------|-------------|
| Active phase only | Only mutate fields belonging to active phase |
| No deletion | Never delete answered questions |
| No rewrite of history | Never rewrite past commits or verification results |
| Append-only | Questions, requirements, verification, commits are append-only (requirements/verification/gates editable during spec phase) |
| Local persistence | Always persist locally after Linear save |

---

## Human Gate Detection

When detecting human signals for phase transitions:

1. **Keyword matching:** Look for trigger keywords in user messages
2. **Explicit confirmation:** Use AskUserQuestion for critical transitions
3. **Phase-gated:** Only accept signals relevant to current phase

**Do not:**
- Auto-advance based on task completion alone (except intake→research)
- Skip confirmation for meaningful phase transitions
- Accept signals for phases we're not ready to enter

---

## Resume Behavior

When `/ticket-work {ticket_id}` is invoked on an existing contract:

1. Fetch ticket, parse contract
2. Validate contract structure
3. **Re-read architecture handshake** (if available):
   - Read `.claude/architecture-handshake.md` using the Read tool
   - If file exists: refresh understanding of repo-specific constraints
   - If file doesn't exist: continue without (graceful fallback)
4. Report current state:
   ```
   Resuming {ticket_id} in {phase} phase.

   Status:
   - Questions: {N} answered / {M} total ({K} pending)
   - Requirements: {N} defined
   - Verification: {N} passed / {M} total
   - Gates: {N} approved / {M} total
   - Commits: {N}

   Next action: {describe what needs to happen in current phase}
   ```
5. Continue from current phase
