name: task-start
description: Start work on an EXISTING work item — load it, cut its canonical branch, move it to in_progress, and note the branch on the item. (Creating a new item is `task-new`.)
steps:
  - ask: { as: issueId, type: text, message: "Work item id?" }
  - do: issue.get
    as: issue
    with:
      id: ${issueId}
  # Engine-enforced guards (decision #19): a failed require STOPS the run before any mutation.
  - require:
      notEquals: ["${issue.role}", "done"]
      message: "${issue.key} is already done — reopen it (task-move) or pick an active item."
  - require:
      truthy: "${issue.branchName}"
      message: "${issue.key} (${issue.nativeType}) has no canonical branch — containers (epic/initiative) and unmapped types are never branched. Pick a child story/task/bug."
  - do: issue.whoami
    as: me
  # Never silently take over SOMEONE ELSE's work. The guard fires only when the item is assigned to
  # a person who isn't the caller — an unassigned item (when: falsy) and your own item (equals) both
  # start freely, so re-running task-start on work you already own stays idempotent.
  # Taking over is an explicit, discoverable act: assign the item to yourself (issue.assign '@me' —
  # what the skill does after asking the human), then start it. No hidden recipe input.
  - require:
      equals: ["${issue.assignee}", "${me}"]
      message: "${issue.key} is assigned to ${issue.assignee}, not you (${me}). Assign it to yourself first to take it over, or pick another item."
    when:
      truthy: "${issue.assignee}"
  # ${issue.branchName} is the core-derived canonical name (<prefix>/<id>-<slug>) — every agent and
  # recipe derives the SAME name for the same item.
  - do: scm.branch.create
    as: branch
    with:
      # fromBranch omitted on purpose — defaults to the repo's default branch (main/release/master).
      name: ${issue.branchName}
  - do: issue.transition
    as: issue
    with:
      id: ${issue.id}
      role: in_progress
  # Starting a task makes it yours: assign the current user (@me). The takeover guard above already
  # cleared any prior assignee, so this reflects who is actually doing the work.
  - do: issue.assign
    as: issue
    with:
      id: ${issue.id}
      assignee: "@me"
  # Post a "started" note on the item so anyone watching sees work has begun — who is on it and where.
  # Gated on branch.created so it fires ONCE (on the real start); a resume stays silent rather than
  # repeating the note on every re-run.
  - do: issue.comment
    when:
      truthy: "${branch.created}"
    with:
      id: ${issue.id}
      body: "▶ Started work — moved to in_progress, assigned to ${me}, on branch `${branch.name}`."
  # Says where the branch IS, not just what it is called. The engine drives provider ports, never the
  # local git working copy, so `scm.branch.create` leaves the caller exactly where they were — and
  # "#N is in progress on <branch>" read as though it had moved them. The cost of that reading is a
  # commit that lands on the default branch, which is not discovered until task-finish fails with the
  # provider's own "No commits between main and <branch>".
  - message: |
      ${issue.key} is in progress on ${branch.name} (created on the provider), assigned to you.
      Your working copy has not moved. To get onto it:
        git fetch && git checkout ${branch.name}
