一键导入
create-ticket
Create a single Linear ticket from args, contract file, or plan milestone with conflict resolution
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Create a single Linear ticket from args, contract file, or plan milestone with conflict resolution
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | create-ticket |
| description | Create a single Linear ticket from args, contract file, or plan milestone with conflict resolution |
Usage:
/create-ticket <title>
/create-ticket --from-contract <path>
/create-ticket --from-plan <path> --milestone <M1-M5>
Arguments:
title - Ticket title (mutually exclusive with --from-contract, --from-plan)--from-contract <path> - Path to YAML contract file--from-plan <path> - Path to plan markdown file--milestone <id> - Milestone ID when using --from-plan (e.g., M4)--repo <label> - Repository label (e.g., omniclaude, omnibase_core)--parent <id> - Parent issue ID for epic relationship (e.g., OMN-1800)--blocked-by <ids> - Comma-separated issue IDs that block this ticket--project <name> - Linear project name--team <name> - Linear team name (default: Omninode)--allow-arch-violation - Bypass architecture dependency validationYou are creating a Linear ticket with standardized format and conflict resolution.
Announce at start: "Creating Linear ticket from {source_type}."
These argument groups are mutually exclusive:
--title (direct title specification)--from-contract (load from YAML contract file)--from-plan + --milestone (extract from plan markdown)If more than one group is provided, report error:
Error: Arguments are mutually exclusive. Provide ONE of:
- --title "Ticket title"
- --from-contract path/to/contract.yaml
- --from-plan path/to/plan.md --milestone M4
--from-plan requires --milestone (e.g., --milestone M4)Error: Must provide ticket source. Use ONE of:
- --title "Ticket title"
- --from-contract path/to/contract.yaml
- --from-plan path/to/plan.md --milestone M4
--title)When --title is provided:
ticket_data = {
"title": args.title,
"repo": args.repo or get_current_repo(),
"requirements": [],
"verification": [],
"context": {}
}
User will need to provide description interactively or via additional args.
--from-contract)Parse YAML contract file matching the ticket-work schema:
def parse_contract_file(path: str) -> dict:
"""Parse YAML contract file for ticket data.
Expected schema (from ticket-work skill):
- ticket_id: optional (will be assigned by Linear)
- title: required
- repo: optional
- requirements[]: list of {id, statement, rationale, acceptance[]}
- verification[]: list of {id, title, kind, command, expected, blocking}
- context: {relevant_files[], patterns_found[], notes}
"""
import yaml
from pathlib import Path
content = Path(path).read_text()
contract = yaml.safe_load(content)
# Validate required fields
if not contract.get("title"):
raise ValueError(f"Contract missing required 'title' field: {path}")
return {
"title": contract["title"],
"repo": contract.get("repo"),
"requirements": contract.get("requirements", []),
"verification": contract.get("verification", []),
"context": contract.get("context", {})
}
Error handling:
--from-plan + --milestone)Extract milestone from plan markdown:
def parse_plan_milestone(path: str, milestone_id: str) -> dict:
"""Extract milestone from plan markdown.
Milestones follow pattern: ## M\d+: Title
Content until next ## heading is the description.
"""
import re
from pathlib import Path
content = Path(path).read_text()
# Pattern: ## M{N}: Title
pattern = rf'^## ({milestone_id}):\s*(.+?)$'
match = re.search(pattern, content, re.MULTILINE | re.IGNORECASE)
if not match:
available = re.findall(r'^## (M\d+):', content, re.MULTILINE)
raise ValueError(
f"Milestone {milestone_id} not found in {path}.\n"
f"Available milestones: {', '.join(available) or 'none'}"
)
milestone_title = match.group(2).strip()
start_pos = match.end()
# Find next ## heading or end of file
next_heading = re.search(r'^## ', content[start_pos:], re.MULTILINE)
if next_heading:
description = content[start_pos:start_pos + next_heading.start()].strip()
else:
description = content[start_pos:].strip()
return {
"title": f"{milestone_id}: {milestone_title}",
"description": description,
"repo": None, # Will use --repo arg or current repo
"requirements": [],
"verification": [],
"context": {}
}
Before creating, search for existing tickets with same title:
mcp__linear-server__list_issues(
query="{ticket_title}",
team="{team}",
limit=10
)
Check if any returned issue has a title that matches (case-insensitive):
def find_existing_ticket(issues: list, title: str) -> dict | None:
"""Find ticket with matching title."""
normalized_title = title.lower().strip()
for issue in issues:
if issue.get("title", "").lower().strip() == normalized_title:
return issue
return None
If existing ticket found, use AskUserQuestion with options:
Found existing ticket "{existing_title}" ({existing_id}).
How would you like to proceed?
1. Update existing ticket (merge descriptions)
2. Cancel old ticket and create new
3. Skip this ticket
Option 1: Update existing
mcp__linear-server__update_issue(id="{existing_id}", description="{merged}")Option 2: Cancel and create new
Option 3: Skip
Generate standardized ticket description:
## Summary
{summary_from_title_or_context}
**Repository**: {repo}
**Dependencies**: {blocked_by_list or "None"}
## Design Decisions
| Decision | Rationale |
|----------|-----------|
| {decision_1} | {rationale_1} |
| ... | ... |
## Files to Create/Modify
- `path/to/file1.py` - Description of changes
- `path/to/file2.py` - Description of changes
## Requirements
{foreach requirement in requirements}
### R{n}: {requirement.statement}
**Rationale**: {requirement.rationale}
**Acceptance Criteria**:
{foreach criterion in requirement.acceptance}
- [ ] {criterion}
{/foreach}
{/foreach}
## Verification
| ID | Title | Kind | Command | Blocking |
|----|-------|------|---------|----------|
{foreach v in verification}
| {v.id} | {v.title} | {v.kind} | `{v.command}` | {v.blocking} |
{/foreach}
## Definition of Done
- [ ] All acceptance criteria verified
- [ ] Verification commands pass
- [ ] Code reviewed and approved
- [ ] Documentation updated (if applicable)
- [ ] No regressions introduced
def build_ticket_description(ticket_data: dict, args) -> str:
"""Build standardized ticket description."""
lines = []
# Summary
lines.append("## Summary\n")
if ticket_data.get("description"):
lines.append(ticket_data["description"])
else:
lines.append(f"Implementation for: {ticket_data['title']}")
lines.append("")
# Metadata
repo = args.repo or ticket_data.get("repo") or get_current_repo()
lines.append(f"**Repository**: {repo}")
blocked_by = [id.strip() for id in args.blocked_by.split(",") if id.strip()] if args.blocked_by else []
deps = ", ".join(blocked_by) if blocked_by else "None"
lines.append(f"**Dependencies**: {deps}")
lines.append("")
# Design Decisions (from context if available)
lines.append("## Design Decisions\n")
lines.append("| Decision | Rationale |")
lines.append("|----------|-----------|")
if ticket_data.get("context", {}).get("patterns_found"):
for pattern in ticket_data["context"]["patterns_found"]:
lines.append(f"| Follow {pattern} pattern | Consistency with codebase |")
else:
lines.append("| *To be determined during implementation* | |")
lines.append("")
# Files to Create/Modify
lines.append("## Files to Create/Modify\n")
if ticket_data.get("context", {}).get("relevant_files"):
for f in ticket_data["context"]["relevant_files"]:
lines.append(f"- `{f}`")
else:
lines.append("- *To be determined during implementation*")
lines.append("")
# Requirements
lines.append("## Requirements\n")
requirements = ticket_data.get("requirements", [])
if requirements:
for i, req in enumerate(requirements, 1):
req_id = req.get("id", f"R{i}")
lines.append(f"### {req_id}: {req.get('statement', 'TBD')}\n")
if req.get("rationale"):
lines.append(f"**Rationale**: {req['rationale']}\n")
lines.append("**Acceptance Criteria**:")
for criterion in req.get("acceptance", []):
lines.append(f"- [ ] {criterion}")
lines.append("")
else:
lines.append("*Requirements to be defined*\n")
# Verification
lines.append("## Verification\n")
verification = ticket_data.get("verification", [])
if verification:
lines.append("| ID | Title | Kind | Command | Blocking |")
lines.append("|----|-------|------|---------|----------|")
for v in verification:
cmd = f"`{v.get('command', 'N/A')}`"
blocking = "Yes" if v.get("blocking", True) else "No"
lines.append(f"| {v.get('id', '-')} | {v.get('title', '-')} | {v.get('kind', '-')} | {cmd} | {blocking} |")
else:
lines.append("*Default verification steps will be used*")
lines.append("")
# Definition of Done
lines.append("## Definition of Done\n")
lines.append("- [ ] All acceptance criteria verified")
lines.append("- [ ] Verification commands pass")
lines.append("- [ ] Code reviewed and approved")
lines.append("- [ ] Documentation updated (if applicable)")
lines.append("- [ ] No regressions introduced")
return "\n".join(lines)
mcp__linear-server__create_issue(
title="{ticket_title}",
team="{team}", # Default: Omninode
description="{generated_description}",
project="{project}", # If --project provided
parentId="{parent}", # If --parent provided
blockedBy=["{id1}", "{id2}"] # If --blocked-by provided
)
mcp__linear-server__update_issue(
id="{existing_ticket_id}",
description="{merged_description}"
)
mcp__linear-server__update_issue(
id="{existing_ticket_id}",
state="Canceled"
)
# Validate mutual exclusivity
sources = [args.title, args.from_contract, args.from_plan]
source_count = sum(1 for s in sources if s)
if source_count == 0:
error("Must provide ticket source")
if source_count > 1:
error("Arguments are mutually exclusive")
if args.from_plan and not args.milestone:
error("--from-plan requires --milestone")
if args.title:
ticket_data = {"title": args.title, ...}
elif args.from_contract:
ticket_data = parse_contract_file(args.from_contract)
elif args.from_plan:
ticket_data = parse_plan_milestone(args.from_plan, args.milestone)
existing = search_existing_tickets(ticket_data["title"], args.team)
if existing:
resolution = ask_user_conflict_resolution(existing)
if resolution == "update":
update_existing_ticket(existing, ticket_data)
return
elif resolution == "cancel_create":
cancel_ticket(existing["id"])
# Continue to create new
elif resolution == "skip":
print(f"Skipped: {ticket_data['title']}")
return
description = build_ticket_description(ticket_data, args)
When --blocked-by is provided, validate that dependencies respect the OmniNode architecture.
Reference: See plugins/onex/lib/dependency_validator.md for validation logic.
# Import validation logic (conceptually - this is documentation)
from lib.dependency_validator import validate_dependencies, filter_errors, filter_warnings, FOUNDATION_REPOS
if args.blocked_by:
blocked_by_ids = [id.strip() for id in args.blocked_by.split(",") if id.strip()]
ticket_repo = args.repo or get_current_repo()
if not ticket_repo:
print("Warning: No --repo specified and unable to detect repository. Skipping architecture validation.")
else:
violations = validate_dependencies(
ticket_repo=ticket_repo,
blocked_by_ids=blocked_by_ids,
fetch_ticket_fn=lambda id: mcp__linear-server__get_issue(id=id)
)
# Filter using ValidationResult severity field
errors = filter_errors(violations)
warnings = filter_warnings(violations)
for w in warnings:
print(f"[WARNING] {w.message}")
if errors:
if not args.allow_arch_violation:
print("\nDependency architecture violations detected:\n")
for err in errors:
print(f" - {err.message}\n")
print("\nValid dependencies flow: app->foundation or foundation->foundation.")
print("To proceed anyway, use --allow-arch-violation flag.")
raise SystemExit(1)
else:
print("\n[WARNING] Proceeding with architecture violations (--allow-arch-violation):\n")
for err in errors:
print(f" - {err.message}\n")
# Append warning to description
description += "\n\n---\n\n**Warning**: This ticket has dependencies that violate architecture guidelines."
Architecture Rules:
| Ticket Repo | Blocked By Repo | Verdict |
|---|---|---|
| application | application | INVALID (app->app) |
| foundation | application | INVALID (foundation->app) |
| application | foundation | VALID |
| foundation | foundation | VALID |
Foundation repos: omnibase_compat, omnibase_core, omnibase_spi, omnibase_infra
# Build create params
params = {
"title": ticket_data["title"],
"team": args.team or "Omninode",
"description": description
}
if args.project:
params["project"] = args.project
if args.parent:
params["parentId"] = args.parent
if args.blocked_by:
params["blockedBy"] = [id.strip() for id in args.blocked_by.split(",") if id.strip()]
result = mcp__linear-server__create_issue(**params)
print(f"""
Ticket created successfully!
ID: {result["identifier"]}
Title: {result["title"]}
URL: {result["url"]}
Team: {args.team or "Omninode"}
Parent: {args.parent or "None"}
Blocked by: {args.blocked_by or "None"}
""")
| Error | Behavior |
|---|---|
| File not found | Report path, suggest checking location |
| YAML parse error | Show line number and syntax issue |
| Missing required field | Report which field is missing |
| Milestone not found | List available milestones in file |
| Architecture violation | List violations, suggest --allow-arch-violation to override |
| Linear API error | Report error, suggest checking permissions |
| Network timeout | Report timeout, suggest retry |
Never:
/create-ticket --title "Add rate limiting to API endpoints" --repo omnibase_core --team Omninode
/create-ticket --from-contract ./specs/rate-limiting.yaml --project "API Improvements"
/create-ticket --from-plan ./EVENT_ALIGNMENT_PLAN.md --milestone M4 --parent OMN-1800
/create-ticket --title "Implement retry logic" --blocked-by OMN-1801,OMN-1802 --team Omninode
/create-ticket --title "Cross-app coordination" --repo omniclaude --blocked-by OMN-1805 --allow-arch-violation
Dispatch a background worker with role-templated prompt and auto-populated collision fences
Comprehensive system health monitoring — checks agent performance, database, Kafka topics, pattern discovery, and service status across the ONEX platform
Orchestrate a Claude Code agent team to autonomously work a Linear epic across multiple repos
Run DoD evidence checks against a ticket contract and generate a verification receipt
Autonomous per-ticket pipeline that chains ticket-work, local-review, PR creation, CI watching, PR review loop, integration verification gate, and auto-merge into a single unattended workflow with Slack notifications and policy guardrails
Full autonomous audit-debug-fix loop for all dashboard pages — Playwright recon, parallel systematic-debug, fix, PR, Linear ticket, re-audit, iterate until clean. Supports local and cloud targets with optional post-fix redeployment.