Skip to content
Back to skills

Mission

ASecurity

Run one coordination tick over dispatched work: read the Mission with mission_get, act on what is unblocked, record it with the mission_* verbs, report one line. Use "mission set <objective>" to declare or re-scope your mission (a structured Harnu Mission), a bare invocation to run a single tick, and "mission done" to ask the operator to close it. Trigger when an orchestration session needs a recurring heartbeat over workers, worktrees and approvals — "run a tick", "how is the mission going",...

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 6, 2026
ai-agentsgo

Works with

  • claude code
  • cli
  • mcp

Security analysis

A100/100

Scanned October 6, 2026

npx -y skills add junielton/harnu --skill mission --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Mission?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Mission
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/junielton-mission/badge)](https://www.skillsdirectory.com/skills/junielton-mission)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: mission
description: Run one coordination tick over dispatched work: read the Mission with mission_get, act on what is unblocked, record it with the mission_* verbs, report one line. Use "mission set <objective>" to declare or re-scope your mission (a structured Harnu Mission), a bare invocation to run a single tick, and "mission done" to ask the operator to close it. Trigger when an orchestration session needs a recurring heartbeat over workers, worktrees and approvals — "run a tick", "how is the mission going", "keep an eye on what we dispatched", "coordinate this" — typically driven on an interval by a loop. Do NOT use to implement work, to review code, or to grade acceptance criteria (that is delivery-verifier).
---

# Mission — the orchestration heartbeat

**Mandatory header.** The one-line `tick —` report expands into the `status`
skill's six-line glance card whenever anything in `Needs you` or `Open` changed
since the previous tick, and `mission done`'s closing summary always opens with
the card. Build it from `mission_get` (plus `gh` for what the mission does not
link) — see that skill for the card's exact shape and rules; this skill does not
restate them.

One mission at a time, **per owner session**, written down. The point: an
orchestrator juggling executors, gates and evidence loses the thread between
interruptions — the Mission is the thread. Any session (this one after
compaction, or a future orchestrator) can read it with one `mission_get` and
resume coordinating in seconds.

## The state is a Mission, not a file

Your mission is a structured Harnu record, stored in the repo's main checkout
under `.harnu/missions/` and reached ONLY through the `mission_*` verbs:

| You want to…                                  | Call                                                   |
| --------------------------------------------- | ------------------------------------------------------ |
| declare the mission and its plan              | `mission_create { steps }`                             |
| read it, with everything Harnu derives live   | `mission_get`                                          |
| see every mission in the repo                 | `mission_list`                                         |
| add a unit of work found mid-mission          | `mission_add_step`                                     |
| tie a step to a child session, worktree, card | `mission_link_child`                                   |
| owe a human sign-off on a step's deliverable  | `mission_add_check`                                    |
| say "I believe this step is done"             | `mission_update_step` with `set: { proof: 'claimed' }` |
| record a verification                         | `mission_verify_step` (never on work you authored)     |
| flag / clear a blocker                        | `mission_set_blocker` / `mission_clear_blocker`        |
| change what "done" means                      | `mission_set_end` (staged; the operator approves)      |
| write the tick's Log line, a child's report   | `mission_log`                                          |
| ask the operator to close                     | `mission_request_close`                                |
| adopt a legacy `.harnu/goals/*.md` file       | `mission_import_legacy`                                |

Never read or edit a `.harnu/missions/*.md` file by hand, and never keep state
the verbs do not hold in your head or in prose — the verbs are the only writers,
and the operator's Topbar pill reads the same record.

**Harnu derives; you do not.** `mission_get`'s `derived` block, its `youItems`
list and its `you` line are computed fresh on every read: the progress, each
linked child's live state, each linked worktree/card/PR resolved, `existence`
proofs, and the deterministic stall flag (`derived.stale`). Read them.
Do not re-derive a stall from Log timestamps, do not poll `get_fleet` for
children the mission already links, and do not recompute a verdict glyph the
projection already gives you.

**`derived.progress` is where the mission is — never recount it.** It carries
`total`, `current` (`{ from, to }`, a range when steps run in parallel, `null`
when all are done), `allDone`, `done`, `verified`, `leftBehind` and
`unprovable`. It counts position, not proof. Say it the way the operator's
Topbar pill does: "Step N of M" with N = `current.from`, "Steps a–b of M" for a
parallel range, "Step M of M ✓" only when `allDone`. Never say "N of M done" or
count proven steps yourself — that number disagrees with the pill.

**The operator's doors are not yours.** A mission is born `active`: the end
question you ask in chat is the agreement, and there is no approval step after
it. Only the operator ends a mission — close as delivered, or discard — and they
can do it at any time; you only ask for the close (`mission done`). After they
end it, every `mission_*` write refuses `MISSION_CLOSED`. On `MISSION_CLOSED`,
stop the loop and report — never retry, never create a replacement mission to
keep ticking.

### Your session id — pass it, and pass your real one

Every verb that takes a session id takes it **on your word**: Harnu's transport
has no per-session identity, so a wrong id is silently believed. Your own id is
already in your context — the scratchpad directory you were given ends in it:
`…/<project-slug>/<SESSION-ID>/scratchpad`. That full UUID is what you pass as
`sessionId` (`mission_create`, `mission_verify_step`, `mission_import_legacy`)
and as `ownerSessionId` (`mission_get`). `<session8>` is its first 8 characters.

- **No scratchpad path in your context** → you cannot own a mission honestly.
  Say so, and ask the operator which session should own it; never invent an id
  or pass a `synthetic-…` one (refused `BAD_SESSION_ID`).
- **That directory reads `agent-<something>`** → you are a subagent, not a
  session. Do NOT create or own a mission. Report back to the session that
  dispatched you.

## `mission set <objective>`

1. **Look for yours first:** `mission_get({ folder, ownerSessionId: <your id> })`.
   One exists for this objective → you are updating it (steps 4–5), not creating
   a second. One exists for an unrelated objective → it must be finished or
   handed back first; say so, do not stack two missions on one owner.
2. **Agree the end — before the first dispatch.** `declaredEnd` is required and
   complete — `kind` (`code`/`ui`/`research`/`decision`/`other`), a concrete
   `target` ("PR merging the 4 slices into main"), and the `evidence` that proves
   it ("green CI + delivery-verifier report, every AC met"). Propose your best
   concrete reading of the objective and confirm it with the operator in ONE
   AskUserQuestion — the proposal is the recommended option. The end question in
   chat is the agreement: the mission is born `active`, and nothing waits on a
   click afterwards.
3. **Create it with the plan in one call — `mission_create { steps }`:**
   `mission_create({ folder, title, declaredEnd, sessionId: <your id>, steps,
scope?, linkedCard? })`. Shape the plan by these rules:
   - **1 unit = 1 PR = 1 step.** One step per deliverable that moves where the
     work is. Sub-phases of a unit (spec, implement, review, ship) are not steps.
     Sub-phases go to the Log (`mission_log`). One executor split across five
     steps makes the pill crawl while nothing moved.
   - **Declare each step's `verification`:** `verifier` for work a child
     delivers, `existence` for an artifact that simply has to exist (a spec, a
     merged PR), `human` only for a stage of its own that gates what comes next
     (an operator decision before the next wave).
   - **Sign-offs become checks, not steps.** "The designer signs off", "DSQA
     done", "validated visually" is a human confirmation of a step's
     deliverable: add it to that step with `mission_add_check({ folder,
missionId, stepId, label })`. Only the operator ticks it. Never add a step
     for it.
   - `scope` takes the repo-relative paths of the spec / PRD / ADR. It is an
     attachment shown next to the progress, never a step. `linkedCard` is the
     epic card slug when there is one.
4. **Link what builds each step:** `mission_link_child` with `{ kind: 'session' |
'worktree' | 'card' | 'pr', ref }` — every child session you dispatch for a
   step, its worktree, its card, its PR once opened. Links are what Harnu derives
   from; an unlinked child is invisible to the mission, and a linked child keeps
   you from being parked while it runs. **Never link a session to a step only to
   change its proof label; link the sessions that built it.** A link is a claim
   about who built the step, and the operator reads it as one.
5. **New work found later** → `mission_add_step` (pass `links` when you already
   know what will prove it). Once the mission has started — any step has a link
   or a proof — `reason` is required: a grown total is always explained.

**Changing an active mission's objective is a re-scope, not a new mission:**
`mission_set_end({ folder, missionId, declaredEnd, reason })` stages the new end
and asks the operator on the `you` line. When they approve, the fixed end's proof
resets to `unproven` — re-verify it.

**A changed rule is a re-scope too, in the same turn.** When the operator changes
how the delivery ends (merge policy, target branch, what counts as done), call
`mission_set_end` in the same turn with the new end; the operator approves the
re-scope in the Topbar. Do not wait for the next tick, and do not leave the old
end standing because the units themselves did not change.

### Adopting a legacy goal file

Missions declared before the `mission_*` verbs live as free markdown in
`<repo>/.harnu/goals/*.md` (or a single `<repo>/.harnu/GOAL.md`). Adopt one ONLY
when it is actually yours:

- Its `session:` frontmatter, or failing that its `<session8>-` filename prefix,
  names YOUR session → yours.
- It names a DIFFERENT session → check that session with `get_session`. Alive or
  merely parked (`hibernated: true`) means the mission is another orchestrator's
  and is not yours to take. Only a session that genuinely no longer exists frees
  it.
- It names no session at all → it is yours only if nothing live is coordinating
  it; when in doubt, ask the operator.

Import it with `mission_import_legacy({ folder, legacyPath, sessionId: <your own
id> })` — **always pass your own `sessionId` explicitly**: most legacy files
carry no session UUID, and without one the call is refused `BAD_SESSION_ID`. Then
`mission_log` whose file it was ("adopted from `<file>`, orchestrated by
`<session8>` / unattributed"). If the ACK's `needsReview` lists `declaredEnd`, the
file stated no usable target: stage a real one with `mission_set_end` at once.
Until the operator approves that re-scope, "review the imported end" sits on
their `you` list.

The import never modifies the source file, and `legacyRaw` keeps it byte for byte
inside the mission. From then on the Mission is the state; leave the source file
where it is. Never write to, archive, or overwrite a goal file whose prefix is
not yours — another orchestrator may be coordinating live behind it.

## `mission` — one tick

1. **Read the mission — one call:** `mission_get({ folder, ownerSessionId: <your
id> })`. It replaces the old hand-assembly of `get_fleet` + `get_approval` +
   board reads: the linked children's live state, the linked worktrees, cards and
   PRs, the proofs, the stall flag and the `you` line all arrive together.

   - Not found → look for a goal file of yours in `.harnu/goals/` and adopt it
     (above); otherwise say "no active mission" and suggest `mission set`.
   - `delivered` → the `you` line asks the operator to close; report that and
     stop the loop.
   - `closed`, or any verb this tick refuses `MISSION_CLOSED` → the operator
     ended it (closed as delivered, or discarded; the Log's last
     `operator` line says which). Stop the loop and report; do not retry.

2. **Read what Harnu derived** — never re-derive it:

   - `derived.progress` — where the mission is: "Step N of M" (N =
     `current.from`), `done`, `verified`, `leftBehind`. Report it as-is.
   - `derived.progress.unprovable` — `existence` steps that are reached or
     current but have no path, card or PR link. Say it in the tick line: "step
     `<title>` can never be proven — link its PR or path", and link it
     (`mission_link_child`) when you know what proves it.
   - `derived.progress.leftBehind` — steps still `todo` behind the current one.
     Each is either work you forgot to dispatch (dispatch it) or a step that no
     longer applies (say so in the Log; the operator sees it at the close).
   - `derived.steps[].children[]` — per linked session: `known` (false = no
     such session: a typo or a vanished child), `taskState`, `hibernated`
     (parked, not dead), `peer`, `pendingApprovals`.
   - `derived.steps[].links[]` — a worktree's `exists`/`branch`/`head` and its
     PRs, a card's board `status`, a PR's `state`.
   - `derived.steps[].existence` — the live proof of an `existence` step.
   - `derived.stale` — `true` means an `active` mission with no evidence for over
     an hour and no linked session working. It is a flag to investigate (a stuck
     child, a lost report), never a reason to respawn.
   - `youItems` / `you` — what the operator owes right now, a list and its
     one-line summary (due checks included). It always prints.

3. **Act only on what is unblocked, respecting the doors:**

   - **Child looks VANISHED → do not re-dispatch on this tick.** Two live
     sessions in one worktree corrupt each other's edits. Prove death first:
     - `known: false` or a missing child can still be a spawn in flight:
       `get_session` reporting `status: "spawning"` or `inflight: true` means it
       is on its way — materialization can land many minutes late. Never retry
       `create_session` into the same folder while it is unresolved; it refuses
       with `SESSION_ALREADY_IN_FLIGHT`, by design.
     - A frozen parent transcript is not death while any file under
       `<transcriptDir>/<sessionId>/subagents/` is recent — a background agent
       may still be pending. Check those mtimes before the verdict.
     - `taskState` lies in both directions. Transcript + worktree +
       subagents-dir is the ground truth.
     - `hibernated: true` is a parked session, not a dead one: the conversation
       is intact and selecting it resumes. It reports no `taskState`; that
       absence is not a stall.

     Re-dispatch only after **two consecutive** checks ~10 minutes apart show
     nothing alive, and prefer a fresh worktree — re-entering the same one needs
     the old session actually STOPPED (an operator click; there is no kill verb).
     Link the replacement with `mission_link_child`. If a retry lands and BOTH
     materialize, escalate at once, naming which one to stop.

   - **A `[Cross-session idle notice]` for one of your children is a tick
     trigger, not noise.** Run this tick's checks against it immediately. If you
     never subscribed at dispatch (`SendMessage { notify_when_idle: true }`), you
     are discovering finished work by polling.

   - **Child FINISHED, pushed, but no PR → dispatch the shipper, this tick.** A
     linked worktree with commits, a clean tree, and no PR for its branch means
     the unit is waiting on YOU: a session dispatched by an agent is spawned
     `agentControlled` and holds no `mcp__harnu__*` verbs, so it never could have
     opened that PR itself. Call `create_session` into that worktree with the PR
     pipeline as `prePrompt`, link the shipper and its PR to the step, and log it.
     Never send the implementer back to review its own diff.

   - **Child FINISHED** → persist its report with `mission_log` (step-scoped),
     mark the step `claimed` with `mission_update_step`, then read the actual
     evidence (diff, gate output, files), never the self-report alone. Verify per
     acceptance criterion with the **delivery-verifier** skill — one AC per call,
     grader blind to the child's report, verdicts `met | unmet | blocked |
needs-human` — and record the result with `mission_verify_step({ folder,
missionId, stepId, verdict, evidence, sessionId })`. **The verifying session
     must not be the step's author**: link the child session to the step first,
     and have a session that is not one of its linked sessions call the verb —
     you, when a child did the work; a separately dispatched verifier, when you
     did it yourself. A self-verification lands `self-verified`, which the
     operator never sees as proven. `unmet` goes back to the SAME child as a
     delta; twice-unmet is an operator decision, not a third attempt.
     `needs-human` passes `checkLabel` naming what the human must confirm: the
     step reads `done` and carries that check for the operator to tick — it
     also goes on the Delivery Report's check-by-hand list.

   - **The operator or the packet asks for a sign-off** ("the designer must sign
     off", "DSQA before it counts") → `mission_add_check` on the step whose
     deliverable it confirms. Sign-offs become checks, not steps: no
     `mission_add_step`, no `human` step. The check lands on the operator's
     `you` list once the step is reached.

   - **Something blocks a step or the mission** → `mission_set_blocker` with the
     reason, what would unblock it, and whose move it is (`agent`/`operator`; an
     operator-owned blocker lands on the `you` line). Cleared →
     `mission_clear_blocker`. A blocker is a flag; it never changes the status.

   - **A turn that ends waiting on the operator raises a blocker.** Before you
     end a turn whose next move is the operator's — a merge, a key, a
     credential, a decision — call
     `mission_set_blocker { owner: 'operator', reason: '<what exactly>', unblocks: '<the observable event>' }`.
     Harnu then chimes, asks for their attention and reminds them every 30
     minutes. Clear it with `mission_clear_blocker` on the turn you see it done.
     Text in the transcript is not a signal: nobody reads it until they come
     back.

   - **The operator changed how the delivery ends** (merge policy, target
     branch, what counts as done) → `mission_set_end` in the same turn, with the
     new end and the operator's words as the `reason`.

   - **New work discovered mid-mission** (a split from verification, a deferred
     fix) → `mission_add_step` with its `reason`, then dispatch and link it like
     any planned unit.

   - **Wrong result** → send the specific delta back to the SAME child, not a
     redo.

4. **Never cross the doors:** no card to `done`, no product-code writes, no
   self-granted execution, no mission ended by you, no check ticked by you. Ping
   the operator only at doors (re-scope approval, a due check, UI approvals,
   Review→Done, the close) or when blocked for more than one full tick — and
   batch the pings.

   **A total stall on the operator is a LOUD event, not a quiet tick.** When the
   ONLY thing blocking all forward motion is a human gesture, raise the
   operator-owned blocker (above) naming the exact action ("merge #68 and #69 —
   both green, the whole train is waiting") — that is what makes Harnu chime and
   re-nudge — then go quiet until the state changes.

5. **Persist the tick:** one `mission_log` line (`<date time> — <state> →
<action taken>`).

6. **Report ONE line** to the user: `tick — Step N of M · <essential state> |
next: <next check>`, the headline taken from `derived.progress`. Silence about healthy in-flight work is the feature; detail lives in
   the mission. The line expands into the `status` glance card when `Needs you`
   or `Open` changed since the last tick.

### Pacing

When the loop paces itself, match the interval to what actually changes:

- Babysitting dispatched Harnu sessions the operator is watching → ~300s, and say so.
- Workers grinding on real code change state every 20–40 minutes → 1200–1800s.
- A gate about to resolve (operator active, approval pending) → 240–270s.
- Everything blocked on an absent operator → 1800–3600s.

Prefer `run_in_background` over long blocking calls while children are live: a
child's message reaches you only between your tool calls.

**A loop you cannot keep alive needs a backstop.** Your ticks live inside a
session that Harnu may park, the operator may close, or a compaction may
interrupt. When the work will outlive a single sitting, have the orchestrator
stand up a `delivery-watchdog` worker on the Scheduler alongside the loop: it is
read-only, reads the same mission through `mission_get`, and notifies the
operator when a unit is parked. See the `orchestrate-delivery` skill.

Honor your own stop condition: when the mission is `delivered` or closed — or
a verb refuses `MISSION_CLOSED` — STOP the loop. A loop that keeps firing after its stop condition burns tokens on
nothing.

## Child reporting — hard rules

Children report to their owner through Claude Code's native `SendMessage` (a
child an agent dispatched holds no `mcp__harnu__*` verbs). A native address is a
**name**, and a name is not an identity: a session's name can change when Harnu
parks and resumes it, so a name captured at dispatch can later point at nobody —
or at somebody else. These rules are not advice; put them in every dispatch
packet, verbatim where they concern the child.

**For the child — must hold, every send:**

1. **Your owner is a session id, not a name.** The packet names it: `owner
session <uuid>`. The name it also gives you is only where that session could
   be reached at dispatch time.
2. **Resolve the name at send time, every time.** Call `ListAgents` immediately
   before sending, and send only to a name you just saw listed that is your
   owner's latest address — the packet's, or a newer one your owner sent you
   since. Never reuse a name from an earlier turn without listing again. If you
   hold Harnu verbs, `get_session({ sessionId: <owner uuid> })` tells you whether
   that id is live or parked before you try.
3. **Name the owner's id in the report itself.** Open every report with `Report
for session <owner uuid> · mission <missionId> · step <stepId>`, so the
   recipient confirms by id, not by the name it arrived under.
4. **Owner unreachable → stop, do not spread.** No name resolves, the send fails,
   or the recipient answers that it is not that session: your report stays in
   `.harnu/REPORT.md` in your worktree (write that file first, always), and if you
   hold Harnu verbs you also record it on your own step with `mission_log({
folder, missionId, stepId, note })`. Then stop.
5. **Never broadcast.** Never send the report — or a summary of it, or a "can
   you relay this" — to any session other than your owner: not a sibling, not
   another orchestrator, not whoever looks nearest. An unreachable owner is a
   wait, not a reason to widen the audience. (What happens after the wait —
   queue, retry, drop with a note — is undecided; the file and the step are the
   fallback until it is.)

**For the owner — must hold:**

- **At dispatch, give the child both halves:** your session id and your current
  native name (the first line of `ListAgents`: "This session is `<name> [<ref>]`"),
  plus the rules above.
- **Re-announce when your name changes.** On every tick, read your own name from
  `ListAgents`' first line and compare it with the last address you recorded
  (`mission_log`: "owner address: `<name> [<ref>]`"). Changed → log the new one
  and send it to each live linked child with `message_session({ sessionId:
<child uuid> })`, which addresses a Harnu-spawned child by its stable id and
  wakes it if parked. A child that is still unreachable falls back to its file,
  which your ticks read anyway.
- **A report is yours when it names your session id** — even when it arrived
  under a name you no longer hold. A report naming a different session id is not
  yours: tell the sender so in one line, and do not act on it or pass it on.

## `mission done`

Verify against evidence, not memory. When every unit is verified and the end's
evidence exists, verify the fixed end (`Delivered and verified`) yourself with
`mission_verify_step({ folder, missionId, stepId: <the fixed end>, verdict: 'met',
evidence, sessionId: <your id> })`. The fixed end's builders are every session
linked to any step; you built none of the steps, so it lands `verified`. Then read
`closeReadiness` on `mission_get` — `null` means the close will land — and call
`mission_request_close({ folder, missionId, sessionId })`. The operator closes.

`closeReadiness` (and the verb) name what is in the way:

- `END_NOT_VERIFIED` → the end is `unproven`, `claimed` or only `self-verified`.
  `self-verified` means no step links a child session, or you linked your own
  session somewhere: link each unit's child session to its step, then verify
  the end again.
- `OPEN_BLOCKERS` → clear or resolve them first.
- `RESCOPE_PENDING` → a re-scope is staged; the operator approves it (which
  resets the end's proof), then verify the end again.
- `MISSION_CLOSED` → the operator already ended it; stop the loop and report.

Due checks, left-behind steps and an unverified end never block the request or
the close: the operator sees them as warnings in the end dialog and decides.
Say which ones remain in your closing summary.

On success the mission is `delivered` and the `you` line asks the operator to
close it — **the close itself is theirs.** Write the final `mission_log` line,
drop a dated summary into project memory (`memory_append`, `decisions` or a
session digest), report the closing summary (opening with the `status` card),
and stop the loop if one is driving the ticks.

## Boundaries

This skill coordinates; it never executes product work — the same contract as the
orchestrator role: delegate, read evidence, gate at the doors. If a tick discovers
work that needs a NEW mission, card it on the board and mention it; do not silently
grow the current mission beyond a logged `mission_add_step`.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…