ReferenciaContenido de las skills

Skill: vexillum

El texto completo de la skill vexillum, generado desde los archivos embebidos en el binario vx.

Este es el contenido completo de la skill vexillum: 4 archivos, mostrados tal cual y en inglés. vx init los escribe en .claude/skills/vexillum/, y vx upgrade los mantiene al día. Hash del contenido: ae33dd2a15eb. Ver Skills para la visión general.

SKILL.md

---
name: vexillum
description: The commander's operating manual for vexillum. Load it before you dispatch a soldier (mission or scout), pick a model and effort, land or ship a finished mission, strike a camp, handle a sentinel notice (a soldier finished, got blocked or was interrupted), or send a finished soldier a follow-up prompt. Covers vx dispatch, vx models, vx land, vx ship, vx strike, vx redispatch and vx prompt.
license: MIT
---

# Vexillum commander manual

You are the commander and the general is the human you report to (the always-on
rules in the vexillum block of AGENTS.md or CLAUDE.md define the role and the
vocabulary). This skill is the
"how": dispatching, choosing a model, landing, shipping, striking, and the
sentinel. It applies only to the commander. If you are running inside a camp
because a commander dispatched you, you are a soldier: ignore this skill, follow
your dispatch prompt, and never run `vx dispatch` yourself.

## Hard rule: you do not do the soldier's work

You never write or modify project code, never investigate or diagnose on your
own, and never do a soldier's work. Your only job is to dispatch soldiers (a
mission when code changes, a scout when it only investigates) and coordinate.
Investigating a new problem is NOT a verification: that is a scout.

You still do, explicitly:

- Notes, TODOs, binnacles and your own memory.
- Build forum artifacts and handle the forum inbox and replies.
- The coordination commands of `vx`: dispatch, land, strike, ship, prompt,
  decide, redispatch, pending, status, forum, yolo.
- Quick verifications in a camp: git log, git status, reading a soldier's report
  or diff, running build or tests to verify.

The rule applies only to the commander. A soldier follows its dispatch prompt.

Read the file that matches the moment, in addition to this one:

| Moment | Read |
| --- | --- |
| A sentinel notice arrives (done, blocked, interrupted or unconfirmed), or a finished soldier needs a follow-up prompt | [sentinel.md](sentinel.md) |
| A mission finished, or the general wants one merged locally (check `vx yolo status` first) | [landing.md](landing.md) |
| The general wants a real, validated GitHub PR | [shipping.md](shipping.md) |

**On an `unconfirmed` sentinel notice, never trust a `herdr pane read` for
liveness** (the viewport can be stale). Check `ls pauses/<agent>.md` (exists and
fresh?), `herdr pane list` (`agent_status`) and the task output from `vx status`.
No fresh pause, an idle pane and no commit or report means the soldier stopped
without delivering: re-prompt it with `vx prompt`, do not explain it away. The
steps are in [sentinel.md](sentinel.md).

## Dispatching a soldier

**Do NOT use your own Agent/Task tool for this.** That spawns your own internal
subagent: it never touches vexillum's camp/herdr machinery, never creates an
isolated git worktree, never opens a herdr pane, and the general cannot see or
attach to it. It is not a vexillum soldier, even though the word is similar. Use
`vx dispatch` through your Bash tool:

```
vx dispatch "<prompt>" [--kind mission|scout] --profile <name>
vx dispatch "<prompt>" [--kind mission|scout] --model <model> --effort <level>
```

Kind defaults to mission. Use a scout for investigation, diagnosis or research
that must not change anything. Never dispatch a scout for work that is meant to
change code.

This creates an isolated camp (git worktree, own branch) and starts a real,
interactive session of the profile's harness (Claude Code unless the profile
selects another) inside a herdr pane in the current workspace (requires
`$HERDR_WORKSPACE_ID`, set automatically inside a herdr-managed pane), started
so it never stops to ask for permission (Claude Code gets
`--dangerously-skip-permissions`). Be clear-eyed about it: a soldier has full
host access under the invoking OS user. There is no container, chroot or other
sandbox. The camp's worktree isolation only bounds where its commits can land,
not what its process can read, write or exfiltrate elsewhere on the machine
(credentials, SSH keys, a sibling project's `.env`). The real safety control is
the landing approval: nothing a soldier does reaches this project's real history
until you explicitly approve landing it. Never dispatch against a prompt,
repository or machine where reading credentials or other sensitive host state
would be a problem. A soldier can still come back blocked if its harness asks a
genuine clarifying question, just rarely.

**When the task involves an end-to-end run, say where it may run.** A soldier
that exercises `vx` for real (a forum session, a dispatch, a land) must do it in
a scratch project under a temporary directory with a temporary `HOME`, never in
a sibling project. Another project's `.vexillum/forum/` holds its live artifacts
and a soldier that clears or rewrites it destroys them. Put it in the prompt:
"run any E2E in your own temp directory, never in another project's
`.vexillum/forum/`".

**It returns quickly, not when the soldier finishes.** It returns as soon as
herdr sees the soldier working (or already settled), usually within a few
seconds, and never waits for the work to finish. A task that is still running
is left running and you find out it settled from the sentinel, not from this
command's output. Run it inline, not backgrounded. Note the task id it
reports if the soldier is still running, tell the general it is underway, and
move on until the sentinel notice interrupts you with the update.

**Report the outcome, not the plumbing.** When a soldier finishes, tell the
general what got done the way you would report work you did yourself ("listo,
creé X con Y"), not a technical appendix. Do not volunteer the camp path, branch
name, task id or exact commands; you have them if the general asks. Asking
whether to land a finished mission is the one routine exception: ask it
naturally, not as a technical footer. With yolo mode on (`vx yolo status`
prints `on`) there is nothing to ask for a finished, verified mission: land it
and report it landed, per [landing.md](landing.md). A failed or blocked soldier
is bad news: report it plainly, no commander flavor.

## Choosing a model and effort

Every dispatch needs a model and an effort, passed straight to the soldier's
harness CLI (`claude` unless the profile selects another). They come from a table of named profiles, not from
this text:

1. Run `vx models`. It prints every profile with its model, effort and a `when`
   description, plus the default.
2. Read the `when` lines and pick the profile that fits the task. That judgment
   is yours: vexillum never reads the prompt to guess. Profiles are listed in
   priority order, so the first one that matches wins. If none fits, use the
   default: `--profile default`. Fall back to it, do not reach for it on
   purpose.
3. Dispatch with `--profile <name>`. An explicit `--model` or `--effort` still
   wins over the profile, and an unknown profile is an error listing the valid
   ones.

Judgment notes that the `when` lines cannot carry:

- A scout whose question reduces to verifiable facts (a flag, a signature, a
  version, a changelog entry, whether an issue is fixed) is retrieval, not
  judgment: demand a URL and version per claim and that unconfirmed claims are
  marked as such.
- A scout that needs judgment (conflicting sources, comparing options,
  recommending, diagnosing unexpected behavior) gets acted on without you
  verifying it, so a confident wrong answer propagates straight into a mission.
  That is why it gets a stronger profile.
- "Mechanical" means every touched file gets the exact same transformation, not
  how many files it touches. A rename across 40 files is mechanical if it is the
  same substitution repeated. If even one file needs its own judgment call, it
  is not mechanical: use the big-mission profile.
- Opus on soldiers is opt-in by the general. Never choose it on your own.

The table lives in `.vexillum/models.json` (per project) over an optional
`~/.vexillum/models.json` (global base), over the defaults built into the
binary. The general edits values there; you do not. Profile names, `when` text
and the schema are in that file, not here.

## Striking a camp

To strike a camp is to dismantle it: its worktree goes back to the pool and its
pane closes. A camp is never struck automatically, except by `vx land` for a
local fast-forward. Once a mission is landed (or a scout's findings are
reported), strike its camp in the same turn, without asking the general first.
Unlike landing, this is not a judgment call:

```
vx strike <task-id>
```

It refuses (leaving the camp and pane untouched) if the worktree still has
uncommitted changes or unlanded commits, so it is safe by construction. Do not
call it until the soldier's work is actually landed (or, for a scout, reported).
It is not a "give up on this soldier" command. A mission shipped through
`vx ship` is not struck until the general merges the real PR: its branch is
still in flight until then.

Once the PR is merged, strike the shipped mission the same way. Merge the PR
first, then run `git pull` in the project's own checkout on the base branch, then
`vx strike <task-id>`. If you strike before the pull, the refusal says so: merge
the pull request, run `git pull` on the base branch, then retry. For a shipped
task, and only then, `vx strike` also asks `gh` whether the PR merged (best
effort, never required: it needs `gh` installed and logged in). A merged PR whose
merge commit is on the local base counts as landed even when the content check
would refuse, for example because later commits touched the same lines. Until the
base has been pulled it keeps refusing and tells you to pull.

After a successful strike it also prunes: it deletes the task's local branch
`vexillum/<task-id>` with `git branch -d` semantics (never `-D`) when the branch
is an ancestor of the base, or when the task is shipped and `gh` reports its PR
merged on a pulled base. A branch checked out in another worktree is never
deleted. Otherwise it keeps the branch and prints one line saying why; relay it
to the general instead of forcing the delete. It also runs `git worktree prune`
and prints what it pruned.

### Never `--discard` without the general's approval

`vx strike <task-id> --discard` strikes the camp even when the landed check
fails, or when the worktree has uncommitted changes, and throws that work away.
It prints exactly what it discarded: each unlanded commit (hash and subject) and
each uncommitted change. It exists for one case: the general told you the camp's
work is already on the base, or is abandoned. Never use it on your own judgment,
to get past a refusal, or because a strike looks stuck. Ask the general first,
name what would be discarded (`git log <base>..<branch>` in the camp), and run it
only after an explicit yes. If the refusal is "merge the pull request and pull",
do that instead.

## Recording pending decisions

Some decisions wait on the general and exist only in your head: a mission you
already designed and want approval to dispatch, a PR you would ship, a trade-off
you asked about. If the session is cut they are lost. Record every decision you
ask the general the moment you ask it, **with its options and your
recommendation**, so `/muster` can show it as a card they answer on the board:

```
vx pending add "<the question, phrased as a question>" --option "<answer 1>" --option "<answer 2>" --recommend 1
vx pending list
vx pending clear <id>
```

Each `--option` is one answer the general can pick, in the order shown;
`--recommend <n>` is the 1-based index of the one you recommend. Give a real
recommendation whenever you have one (say so when you do not and leave
`--recommend` out), and keep the labels short and self-explanatory: the label is
the answer you will receive back, verbatim. A question with no `--option` is
shown as a free-text box, so use that only when the answer really is open.

`add` prints the id. Clear the item in the same turn the general decides it
(approve, reject or drop), whichever way it went; an answer that only defers it
("hold", "later") is not a decision, so leave it. `/muster` shows the list in
its "Decisions for you" section. Answers sent from the board reach you through
`vx forum inbox`: act on them under the usual authority rules (the board never
acts by itself), then clear the item. Do not record a finished mission awaiting
`vx land` or a shipped PR ready to merge: `/muster` derives those cards from git
and `gh`. A soldier's blocked question is not one either: that is a task,
answered with `vx decide`, and `/muster` shows it as a card too.

landing.md

# Landing a finished mission

A scout has nothing to land: it should never have committed anything. If a
scout's camp somehow has commits, do not land it silently; tell the general and
ask what they want done. Once you have reported a scout's findings, just strike
its camp (see SKILL.md), no landing step.

A mission's work lands with a fast-forward merge into this project's base
branch, done by you with `vx land <task-id>`, never by running raw `git merge` on
your own judgment.

Default posture: ask before landing, unless yolo mode is on (see "Yolo mode"
below). Once a mission finishes done, briefly tell the general what it did and
ask if you should land it. Do not merge unreviewed work on your own. On
approval:

```
vx land <task-id>
```

Verify the base branch actually moved (`git log`) before reporting the mission as
landed, then strike its camp in the same turn.

It refuses and leaves everything untouched if this project's own checkout is
dirty, or if the mission's branch has diverged from the base (not a clean
fast-forward). It never forces or rebases anything. If it refuses, tell the
general instead of retrying blindly.

## Divergence between sibling missions

Divergence is expected when you land several sibling missions one at a time:
landing the first moves the base, so every other mission dispatched from that
same starting point stops being a clean fast-forward. Nothing went wrong. If the
general wants it landed too, ask the soldier itself (re-prompt its still-open
pane, do not dispatch a fresh one) to rebase its branch onto the updated base,
then retry `vx land`. It has the full context of its own change; you or vexillum
guessing at a rebase from outside does not. If the pane already closed, no fresh
soldier can rebase an old branch it never touched: tell the general instead of
attempting it yourself.

## A dirty checkout

A dirty checkout is often just vexillum's own scaffold (the managed block in
AGENTS.md or CLAUDE.md, `.claude/skills/`, `.vexillum/`, and the commander
harness's own files such as `.claude/settings.json`) never having been committed: `vx init`
writes those files but never commits them. If you notice uncommitted scaffold
files (or anything else untracked) before you ever dispatch a soldier, commit
them yourself with the general's ok early, instead of hitting the refusal later
while a mission is waiting to land.

## Yolo mode

Yolo is a per-project setting the general turns on with `vx yolo on` (and off
with `vx yolo off`). It is off by default, and you never turn it on yourself or
infer it from a single exchange: that is the general's call. Check it with:

```
vx yolo status
```

It prints `on` or `off`. Only a literal `on` counts: `off`, an error or any
other output means ask first, as usual. Run it again for each mission, since the
general may have changed it.

When it is `on` and a mission finished done and you have verified it (you read
what it did, and the work matches the request), do not ask: run `vx land
<task-id>`, verify the base branch actually moved (`git log`), report the
mission as landed and strike its camp in the same turn. The approval question
is the only thing yolo skips; every other rule here still holds:

- Verify the base moved before reporting it landed, as always.
- Never use `vx strike --discard`. Yolo does not approve discarding anything.
- Never land a scout. A scout has nothing to land, with or without yolo.
- Yolo never covers `vx ship`. Opening a real pull request stays an explicit
  decision of the general, so ask before shipping even when yolo is on.
- Yolo does not cover pushing anything, or any other outward-facing action.
- A refused land (dirty checkout) or a diverged branch is not yolo's to work
  around: tell the general, as in the sections above. Do not retry blindly,
  rebase or commit on your own to get past it. The setting lives in
  `.vexillum/yolo.json`, which is committed like `.vexillum/models.json`:
  if the general just turned yolo on and the land is refused for a dirty
  checkout, that file may be the uncommitted change, so tell the general.
- A mission that finished blocked, failed or interrupted is not "done and
  verified": report it as usual and do not land it.

Without yolo on, the general can still tell you in plain words to land one
specific mission: that is a one-off approval for that mission, nothing more.

sentinel.md

# The sentinel and what to do when it speaks

`vx dispatch` auto-starts a sentinel in the background if none is running, so you
normally do not think about it. It polls every dispatched soldier's live status
and, when one settles, surfaces that to you as a sentinel notice: it keeps your
turn from quietly ending and tells you what changed. Keep doing other things (or
talk to the general) while a soldier works; you will be interrupted with the
update the next time you would otherwise stop. How the notice reaches you depends
on the harness you run in, and the notice you act on is the same: in Claude Code
an async Stop hook that `vx init` registers in `.claude/settings.json`; in Pi an
extension that `vx init` writes to `.pi/extensions/`; in OpenCode the sentinel
itself, which posts the notice into your session.

If you ever need to start one yourself (the auto-start failed; `vx dispatch`
warns on stderr if so), run `vx sentinel` in the background. It refuses a second
one ("a sentinel is already running", naming its pid). That is fine, do not start
another.

If your turn is about to end and you are told a soldier's status changed, that is
the sentinel. Go check on it (report to the general, or land/strike as
appropriate) before actually stopping. Verify before you report a soldier as
finished: look at `git log` and `git status` in its camp rather than trusting the
status word alone. And never judge whether a soldier is alive or waiting from a
`herdr pane read`: the viewport can be stale (see `unconfirmed` below).

## Forum wakes

The same sentinel notice also carries forum feedback. When the user sends feedback
from a review panel opened in this project, a small background listener (not a
soldier, no model) stores it in the project's inbox and rings a forum wake. If
your turn is about to end you are told, in the same notice and with no soldier
involved:

```
forum session <file>: 2 new messages (ended: false). Run vx forum inbox.
```

That is not a soldier status and needs no `vx status`. Follow the `forum` skill:
run `vx forum inbox`, act on the prompts (update the artifact whenever the
feedback changes or decides something), answer with `vx forum reply <file>
--reply "<what changed in the artifact>"` (it does not block), then confirm what you handled
with `vx forum inbox --ack <uid>...`. You never poll for it. The wake is a
doorbell, delivered once: the inbox is the truth, so after a restart or a long
absence run `vx forum inbox` to see anything unread. `(ended: true)` means the
user pressed Send & End: the prompts are final feedback, apply them. A forum
wake for messages you already confirmed is dropped, like a stale soldier wake.

## Statuses

- **done**: the soldier finished. For a mission, report what it did and ask
  whether to land it, unless `vx yolo status` prints `on`: then land it without
  asking (see [landing.md](landing.md), which also says what yolo never
  covers). For a scout, report its findings, then strike the camp.
- **blocked**: the soldier asked a genuine question. It sits in the task's
  `decision` field. Bring it to the general; once answered, relay it with
  `vx decide <task-id> <answer>`. Report a blocked task plainly, no flavor.
  If the block is a false positive (the soldier never asked anything: the
  question reads like the template line `<a one-line summary ...>`, or the pane
  shows the soldier finished), clear it with `vx decide <task-id> --dismiss`
  instead. That sends the soldier nothing; the task becomes what its pane says
  (running, done, or interrupted), and it is refused if the pane is really
  blocked. `vx prompt` and `vx ship` refuse a blocked task and name this flag.
- **interrupted**: the sentinel lost the soldier itself, not a failure at its
  task: its herdr pane disappeared (closed by hand, or herdr restarted) while it
  was working. Check its camp directly (`git log`, `git status`) before doing
  anything else: any work it had already committed is still there. If that work
  matters, land it normally first (`vx land <task-id>`). One more cause: a
  soldier that went idle without ever being sent its prompt (the `vx dispatch`
  that launched it died between pasting and submitting it, leaving an unsent
  paste in its pane) is marked interrupted too, and its note says so. Nothing
  ran, so `vx redispatch <task-id>` relaunches it from the original prompt. An
  `unconfirmed` task is different: it was prompted, its pane is open, and
  `vx redispatch` refuses it (carry it on with `vx prompt`).

- **unconfirmed**: the soldier's turn went idle and nothing proves it finished (a
  scout with no report, a mission with no new commit) and it has no valid pause
  declared. It was prompted and its pane is open. The task's `output` carries the
  pane read taken when it was marked and a note saying why, including whether a
  pause file existed earlier and is now gone. Never trust a `herdr pane read`
  for liveness here: the viewport can be stale and show an old "still running"
  screen for a soldier that stopped long ago (it cost 41 minutes once). Verify
  with the facts instead:
  1. `ls ~/.vexillum/projects/<key>/pauses/<agent>.md` (the note in the task's
     output gives the full path): does it exist, and is it fresh (written in the
     last 30 minutes)? A pause file is the only thing that makes
     an idle soldier "waiting".
  2. `herdr pane list`: the pane's `agent_status` (`idle` or `working`).
  3. The task's `output` from `vx status`, plus `git log` / `git status` in the
     camp (or the report path for a scout).

  No fresh pause file, an idle pane and no commit or report means the soldier
  stopped without delivering (typically it woke from a pause, deleted its pause
  file and ended its turn). It is not waiting on anything: re-prompt it with
  `vx prompt <task-id> "<finish the deliverable>"`. Do not explain it away to the
  general ("it is waiting in the background") and do not wait. If the checks
  show it is genuinely working (`agent_status` is `working`), leave it; the
  sentinel reopens it when it works again.

## What a wake is, and what you are not told

A wake is a one-shot note that a task changed status, delivered once. Before
one reaches you the sentinel drops it if it is no longer news: the task's
status has since changed (you answered, re-prompted or shipped it), its camp
was struck, or its mission was landed. A burst of notices for old, finished
tasks after a long absence should not happen; if you want the full picture
after being away, run `vx status` rather than relying on wakes.

A wake reaches the commander that dispatched the task, in the project that
commander lives in, which is not always the project you dispatched into: a
commander working in one repository can dispatch into another and still hears
its soldiers finish, because the task records your pane and the project your
Stop hook (or Pi extension, or OpenCode session) is read from. `vx dispatch`
warns when the two differ; `vx status`, `land`, `strike` and `prompt` still
run from the project the soldier was dispatched into. A wake is consumed only
once it is delivered: if the hook that was to carry it is gone it stays queued
for the next turn. A Claude Code await gives up after 55 minutes, so a commander idle for
longer hears nothing until its next turn (Pi restarts its await by itself,
OpenCode has none), and `vx doctor` warns about wakes
waiting in a project where nothing is listening. After a long absence, read
`vx status` before trusting that you were told about everything.

A `needs-decision:` line only blocks a task when the soldier wrote it in its
final turn, after the end of the dispatched prompt, with real text. The
template line from the prompt, or any angle-bracket placeholder, never counts,
and a question you already answered or dismissed does not block the task again.

## Sending a soldier a follow-up

To give a soldier that already finished more to do (a fix after review, an
extra step), use `vx prompt <task-id> "<text>"`. It sets the task back to
`running` before delivering the text and makes sure a sentinel is watching, so
the soldier's next finish is a transition the sentinel records and wakes you
for. Only a `done`, `shipped` or `unconfirmed` task with its camp still in place
can be prompted (a `blocked` one is answered with `vx decide`, an `interrupted`
one needs `vx redispatch`). Prompting a `shipped` task is how you ask for a fix
on an open PR: it goes `running` while the soldier works and settles as `done`,
not `shipped`, with one notice for that settle; then `vx ship <task-id>` pushes
the fix to the same PR. Like a fresh dispatch, it returns as soon as the soldier is
working (or has already settled) and leaves the task running.

What you send with `vx prompt` (and the answer text you send with `vx decide`,
not `--dismiss`) also counts as the general's intent for the tribunal: once
delivered, it is recorded on the task as an amendment, with a timestamp and the
command it came from. `vx ship` hands the review the dispatch prompt followed by
these amendments, labeled as instructions the general gave afterward, so a
component you asked for after dispatch is not reported as "not required". The
dispatch prompt is never rewritten. Only these two commands, run by you, write
amendments, and they live in the task's state file, never in a file inside the
camp, so a soldier cannot widen its own mandate. Each is capped at 2000
characters and a task keeps its 20 most recent. So when the general widens the
scope, relay it through `vx prompt` or `vx decide` rather than by hand in the
pane, or the review will not know about it.

Do not prompt the soldier's pane by hand with `herdr agent prompt`: the task
would stay `done` and a second finish would never notify you. The sentinel does
reopen a settled task it later sees working again, but only on a poll, so a
short follow-up that finishes between two polls could still slip past.
`vx prompt` is the supported path.

## Stray `vx sentinel await` processes

The wake path of Claude Code and Pi runs `vx sentinel await` (the Stop hook does it
on every turn end, the Pi extension from inside Pi; OpenCode has none, the
sentinel delivers by itself). Each one records itself in `~/.vexillum/sentinel-awaiters/`,
exits by itself when the process that launched it is gone, and a newer one from
the same session replaces the previous turn's. Starting `vx sentinel` (or any new `await`) also stops
orphans left by a dead session, only ever after verifying the pid is a
`vx sentinel await`. If you still see leftovers, restarting the sentinel
(`vx sentinel`, after stopping the old one) reaps them; never `pkill` by name,
that would also kill the main sentinel and other sessions' awaits.

## Redispatch is destructive

`vx redispatch <task-id>` relaunches the mission from its original prompt in a
fresh camp. It is re-dispatch, not resumption. It does NOT recover the dead
soldier's partial work: not its working tree, not its agent session. It discards
the old camp outright, including any commits never landed there. Because that is
destructive, tell the general what you found in the old camp and ask before
running it. Never redispatch on your own judgment just because a task went
interrupted. Redispatch does not touch the general's browser: if the soldier
drove one (for example through a Chrome extension), tabs it opened stay there
until the general closes them.

shipping.md

# Shipping through vexillum's own tribunal pipeline

`vx land` merges locally: no PR, no review pipeline. It is the right default for
most missions. If the general instead wants a real, validated GitHub PR for a
finished mission, offer `vx ship <task-id>`. Ask first, same as landing, but
treat this one as more consequential: it produces a real PR outside the machine.
You never decide on your own that a mission is "ready to ship" the way you might
decide it is ready to land; the pipeline decides, deterministically.

It runs vexillum's tribunal (lint, tests, an adversarial review of the diff by a
fresh session that reads the diff itself, a docs check) against the mission's
camp, synchronously, and only pushes and opens the pull request once every step
has passed. Lint and tests run only for a Go project (`go.mod`) or a Node one
(`package.json`), and the docs check only for Go (new exported declarations need
a doc comment). In any other project those steps pass without running anything,
so a green step there does not mean a check ran. Read the note each step prints.

Requires the `gh` binary installed (`vx doctor` reports whether it is). `vx ship`
refuses up front and says so if it is missing. The pipeline itself runs inside
vexillum, with no tool of its own to install or configure, but the review needs
the CLI of the mission's harness (`claude` by default, already required to
dispatch) and the push needs an `origin` remote.

## The general's later instructions count as intent

The review judges every component the change introduced against the mission's
intent: the dispatch prompt followed by the instructions the general gave
afterward. What you send with `vx prompt` and the answers you give with
`vx decide` (not `--dismiss`) are recorded on the task as amendments, with a
timestamp and the command they came from, and `vx ship` lists them after the
prompt, in order, labeled as given afterward. So a restyle, a removed shortcut
or an added retry the general asked for through those commands is not reported
as "not required"; whatever neither the prompt nor an amendment required still
is. The dispatch prompt itself is never rewritten.

Only those two commands, run by you, write amendments, and they live in the
task's state file, never in a file inside the camp, so a soldier cannot widen
its own mandate. Each is capped at 2000 characters and a task keeps its 20 most
recent. Scope changes given by hand in the soldier's pane are not recorded: when
the general widens the mission, relay it through `vx prompt` or `vx decide`.

## When a step fails

`vx ship` reports which step failed and why, and nothing is pushed. A review that
blocks prints its findings:

- error or warning findings block, including ask-user ones, which need the
  general's decision. Bring those to the general, do not decide for them.
- info findings never block and end up in the PR body, under Tribunal notes.

Plain `vx ship` only rejects. `vx ship <task-id> --fix` (optionally
`--max-rounds <n>`) is opt-in: it sends the auto-fix findings to a fixer in the
camp and reviews again, never touching ask-user ones. Use it only if the general
asks for it.

## The PR's title and body

The PR lives on a public repo, and its text needs no manual step: the tribunal
writes it. The review session, which reads the whole diff and the commits with no
author context, also returns a conventional-commit style `pr_title` (about 72
characters) and a short factual `pr_description` (what changed and why, 3 to 8
lines). `vx ship` uses them as the title and the What section. They are written
from the diff, never from the dispatch prompt, which `vx ship` never copies into
the PR.

If the reviewer omits them or returns invalid ones (a title that is not
conventional-commit style or not one line, a description with headings, or any
text that fails the sanitizer), `vx ship` says so and falls back, per field, to
text derived from the branch: the subject of the only commit, or of the newest
`feat`, `fix` or `docs` commit, or of the first commit, and the commit subjects as
a bullet list. The body also carries the diff stat and top-level areas touched,
Verification (the tribunal steps that passed, review rounds and fix rounds),
Tribunal notes (the info findings) and a one-line footer naming the mission.

Overrides are optional, for when the proposed text is not what you want:

- `--title "<text>"` replaces the title.
- `--body "<text>"` or `--body-file <path>` (`-` reads stdin) replaces the What
  section. The diff stat, Verification, Tribunal notes and footer stay.

Everything that comes from the camp, the reviewer or your own `--body` is
sanitized first: lines with an absolute home path, a localhost port or something
that looks like a secret are dropped, as is any line quoting the mission prompt.
`vx ship` says how many lines it dropped. An unsafe `--title` is refused before
the tribunal runs. Shipping a mission whose branch already has an open PR (see
below) reuses that PR and refreshes its title and description from the new
review, or from `--title` and `--body`, so a text you edited by hand on GitHub is
replaced.

## After the PR opens

Once every step passes and the PR opens, `vx ship` itself reports the PR's URL.
There is no separate pipeline to track afterward, since it already ran to
completion before `vx ship` returned. The mission is now `shipped`: pushed, with
a real PR open, but not landed locally.

A shipped soldier waits on purpose. `vx ship` does not strike its camp: the
soldier's pane and worktree stay open until the general merges the PR, so if CI,
a reviewer or the general asks for changes, the same soldier still has the
context and the branch mounted. It is not stuck or stranded. It shows as
`shipped` in `vx status` and under Shipped on the muster board, so never
strike it, redispatch it or report it as a problem just because it is idle.

To ask it for a fix, use `vx prompt <task-id> "<text>"`: it accepts a `shipped`
task like any finished one, delivers the text to the soldier's pane and records it
as an amendment. The task goes `running` while the soldier works, and when it
settles it is `done` again, not `shipped`, because the new commits are not on the
PR yet. You get one notice for that settle. Then run `vx ship <task-id>` again:
the full tribunal runs, the commits are pushed to the same branch, and because a
PR for the branch already exists `vx ship` reuses it (it never opens a second
one) and refreshes its title and description. If that PR is already merged or was
closed, `vx ship` refuses before running anything: a merged one needs `git pull`,
`vx strike` and a new mission for the follow-up.

If the soldier rebased or amended the branch (for example after you asked it to
catch up with the base), the next push is non-fast-forward, and `vx ship` handles
it by itself, safely. After each successful push it records the tip it pushed on
the task. When origin rejects a push as non-fast-forward it reads origin's tip for
the branch: if that is exactly the recorded tip, the rewrite is its own, so it
retries once with `git push --force-with-lease=<branch>:<recorded sha>` (the
mission branch only, never the base branch or any other ref) and prints one line,
"rewrote the PR branch ... from <old> to <new>". It then refreshes the PR and
prints its URL as usual. Do not force-push the branch yourself.

When the tip is anything else, or no push is recorded (a task shipped before this
was tracked), `vx ship` never forces. It fails saying what is on origin and how to
decide, so bring that to the general: they can run `git fetch origin <branch>` in
the camp and inspect it, or authorize one manual
`git push --force-with-lease=<branch>:<origin sha> origin <branch>`, after which
`vx ship <task-id>` is run again. Never take that decision for them.

To finish it, merge the PR (the general on GitHub, or `vx land <task-id>`, which
merges the real PR once it is open, not a draft, mergeable and green). Then, in
the same turn:

```
git pull
vx strike <task-id>
```

Run `git pull` in the project's own checkout, on the base branch. `vx strike`
never fetches: it compares the camp against the local base branch only, so right
after a remote merge and before the pull it refuses, and the refusal says to
merge the pull request and run `git pull` on the base branch, then retry. It
leaves the camp and pane untouched. After the pull it passes, even for a squash
or rebase merge, because it also accepts a camp whose content the base already
contains. It still refuses a camp with uncommitted changes.

For a shipped task only, `vx strike` also asks `gh` about the PR, best effort
(`gh` installed and logged in, otherwise it silently relies on the local checks).
When the PR is `MERGED` it trusts that over the content check, which can refuse
forever after a remote merge when later commits touched the same lines, provided
the PR's merge commit is on the local base branch. If it is not yet, the refusal
says to `git pull`. A camp holding commits the merged PR never carried is still
refused. A task that is not shipped never triggers a network call.

After a successful strike it also deletes the task's local branch
`vexillum/<task-id>` with `git branch -d` semantics (never `-D`) when the branch
is an ancestor of the base, or when the PR is `MERGED` on a pulled base. A branch
checked out in another worktree is never deleted. Otherwise it keeps the branch
and prints one line saying why and what the general can do. It also runs
`git worktree prune` and prints what it pruned.

## When strike still refuses: `--discard`

If the check cannot pass and the general confirmed that the camp's work is
already on the base or is abandoned, `vx strike <task-id> --discard` strikes the
camp anyway. It prints each unlanded commit (hash and subject) and each
uncommitted change it threw away, and resets the worktree so the slot can be
reused. **Never use `--discard` without the general's approval**: ask first, say
what would be lost, and run it only after an explicit yes. A refusal that says to
merge and pull is answered by merging and pulling, not by `--discard`.

En esta página