---
name: start-session
description: Open a PostMQ build session — read the project's context files, recover where the last session stopped (active session, recent decisions, backlog counts), surface drift against the main branch, then resume or start a session. Run BEFORE any material work — a new feature, a behaviour-changing fix, a schema or migration change, a public-contract change, or a scope change.
---

# start-session

Recovers where the last session stopped, then opens (or resumes) a build session so everything you
do next is attributed to it.

Session state lives in PostMQ, not in files. Nothing here writes a local ledger.

## When to run it

Before **any material work**: a new feature, a fix that changes behaviour, a schema or migration
change, a public API or wire-contract change, a security-posture change, a scope change.

Not before answering a question, reading code, or a trivial typo fix.

If you pivot mid-session to unrelated work, that is a new session — run this again.

## Steps

1. **Read the project's own rules.** Whatever this repository uses — `CLAUDE.md`, `AGENTS.md`,
   `CONTRIBUTING.md`, a `docs/` convention file. Re-read them; they change between sessions and a
   remembered version is the one that is wrong.

2. **Recover where the last session stopped.** Three calls, in this order:

   - `mcp__postmq__list_build_sessions` with `status="Active"` — the resume target.
   - `mcp__postmq__list_decision_log` with `limit=10` — what was decided recently, and why.
   - `mcp__postmq__count_backlog_items_by_status` — what is open.

   If an active session exists, read its `current_state_markdown` first. That is the lead the last
   session left for you, and it is the single highest-value thing on this list.

3. **Surface drift.** Compare the age of the main branch's last commit against the most recent
   decision-log entry and build-session close. If anything is more than a day stale and you are
   about to do material work, say so before starting — a stale picture is how two sessions do the
   same work twice.

   Also check what is already in flight: open pull requests, and any other working copies with
   uncommitted changes. Starting work a peer session has already shipped is the most common way
   this goes wrong.

   On most clients a `SessionStart` hook has already printed this block before you read it. **On
   Antigravity nothing has**, because the kit wires no hook there — so run it yourself, once, and read what
   it prints:

   ```bash
   .agents/tools/postmq-session-start.sh
   ```

   Run it from the **repository root** — the path is relative, so it will not resolve from a
   subdirectory (a worktree root is fine; the kit installs into each). If the file is not there at
   all, the kit's session-state component is not installed for this client; gather the same signals
   by hand rather than skipping the step.

4. **Pick a branch.** Material work belongs on its own branch off the main branch, not on main.

5. **Resume or start the session.**

   - If an active session matches the work you are about to do, resume it and
     `mcp__postmq__ping_build_session` to show it is alive.
   - Otherwise `mcp__postmq__start_build_session` with `computer`, `branch`, `worktree_path` and a
     specific `intent` — what you are about to do and why, not a restatement of the ticket title.

   `start_build_session` is **resume-or-create**: the same project, computer, branch and actor gets
   the same session back (200) rather than a second one (201). You do not have to check first.

   If the usage reporter is installed, write the returned id to
   `<state-dir>/active-build-session-<this session's id>` — `.claude/state/` in Claude Code,
   `.codex/state/` in Codex, `.zcode/state/` in ZCode, `.agents/state/` in Antigravity. That file is
   the only way the reporter knows which build session the transcript it just summed belongs to;
   without it the usage is read, and then dropped.

   **Anchor it at the git common directory's parent, not at the current directory.** That is where
   the reporter looks, and the two are the same everywhere except a worktree — where a relative
   path lands inside the worktree and the reporter, searching the main checkout, finds nothing. The
   failure is silent: the session simply reports no usage.

   ```bash
   state_dir="$(dirname "$(git rev-parse --path-format=absolute --git-common-dir)")/.agents/state"
   mkdir -p "$state_dir"
   printf '%s' "<build session id>" > "$state_dir/active-build-session-<this session's id>"
   ```

   (Swap `.agents` for your client's directory from the list above.) Write the one file, never a
   glob — that directory is shared by every worktree and every session running against this
   repository.

   The name has to carry **this session's own id**, because that is what the reporter looks the file
   up by. In Claude Code it is `$CLAUDE_CODE_SESSION_ID`. In ZCode that variable is set for hooks and
   not for the shell you run commands in, so the SessionStart hook prints the id in its `== zcode
   session ==` block instead — take it from there rather than guessing, and if the block is absent
   (the hook is not installed, or not yet approved) say so rather than writing a name nothing reads.
   In Antigravity nothing puts the id in the environment either, and unlike ZCode there is no hook to
   print it. Ask the reporter, which reads it out of the conversation database:

   ```bash
   python3 .agents/tools/postmq-usage-reporter.py --antigravity --dry-run
   ```

   Its `client_session_id` is the id to name the pointer file with.

   If it reports that it found no conversation — no workspace recorded, or two written too close
   together to tell apart — do **not** skip the pointer and move on. Find the database yourself
   (`~/.gemini/antigravity/conversations/`, or `~/.gemini/antigravity-cli/…` for the CLI; newest
   first) and name it, which gives you the id and settles which conversation this is:

   ```bash
   python3 .agents/tools/postmq-usage-reporter.py --antigravity <path-to.db> --dry-run
   ```

   Only if you genuinely cannot identify the conversation, say so and skip the pointer — and say it
   plainly, because end-session then has nothing to report against unless it is given the build
   session id by hand.

6. **Ask which rules apply — before you touch a file.** `mcp__postmq__query_applicable_rules`,
   passing the file paths, operations, languages, code patterns and project attributes of the change
   you are about to make. Only the rules whose every populated trigger dimension matches come back,
   so the answer is short enough to actually read.

## While the session runs

**Record each PR the moment you open it** — `mcp__postmq__record_build_session_prs` with the session
id and `related_prs`, each entry needing only a `url`. It works on an active session and REPLACES the
list, so send every PR the session has produced so far; re-sending an identical list changes nothing.

This is a standing instruction rather than a closing step, and the difference is the whole point. The
reason to record a PR at the moment it exists is that a session which crashes, exhausts its context,
or is simply abandoned never reaches its close — so a close-time-only record loses exactly the
sessions whose work is hardest to find again. An instruction that lived only in the closing skill
would be loaded only by sessions that were going to be fine anyway.

`end-session` records them again at the close. That is reconciliation, not the primary path.

## What good looks like

You should be able to answer, out loud, before writing any code: what the last session decided, what
is still open, what is in flight elsewhere, and which rules apply to the change you are about to
make. If you cannot, you have not finished this skill.

## If the calls fail

Every `mcp__postmq__*` call is scoped by the credential. If they fail closed, the credential is
missing, expired, or scoped to a different workspace — fix that rather than proceeding without a
session. Writes need the `write_session_state` scope; reads need only an authenticated caller.

Skipping the protocol is itself a decision. Never skip it silently — say that you are skipping it,
and why.
