---
name: portfolio
description: Cross-project state board. Use when the user invokes /portfolio, asks "what's the state of my projects / what needs me", parks or wakes or kills a project, or at the close of any session that materially changed a project's status. The board lives at ~/PORTFOLIO.md (change the path to suit your setup).
---

# Portfolio — cross-project state board

The board is `~/PORTFOLIO.md`. It is a registry with pointers, not a
mirror — per-project truth stays in each repo's own state files. Never duplicate
project detail into the board; link to it.

## Actions

**Status (default, no args):** Read the board. Run every liveness check in the
"Liveness checks" table that has a defined command. Report: what's broken, what's
blocked on the user, which parked wake-conditions have come due (check dates against
today), and anything in Triage still unclassified. Lead with the items needing a
decision.

If you run a nightly verifier that writes briefings on each machine, also pull the
latest briefing headline from each box. Never hang on a dead box — always
`-o ConnectTimeout=10`:

```
ssh -o ConnectTimeout=10 <box> 'b=$(ls -t ~/briefings/*.md 2>/dev/null | head -1); if [ -n "$b" ]; then echo "== $(basename "$b")"; head -15 "$b"; else echo "no briefings yet"; fi'
```

Degrade gracefully, one line per box, never fail the status run:
- ssh times out / box offline → report "<box>: unreachable".
- "no briefings yet" (dir missing or empty — e.g. the verifier is not installed yet,
  or its first run hasn't fired) → report that, not an error.
- Otherwise report the briefing date and its finding lines (briefings lead with
  exceptions, so the head IS the headline). Flag a latest briefing older than
  2 days — the verifier itself may be down.

**`park <project> [reason]`:** Move the project's row to Parked. A wake condition
is REQUIRED — if the user gave none, ask for one ("explicit pick-up" is acceptable
but say so). Convert relative dates to absolute.

**`wake <project>`:** Move to Active. Read the project's own state file
(`.agents/STATE.md` or equivalent) and summarise where it left off so the session
can continue.

**`kill <project>`:** Confirm first — killing is a verdict, not a park. Then move
to a Killed section (create it on first use) with one line on why. Do not delete
the repo; that is the user's call, separately.

**Session close:** When a session materially changed a project (milestone done,
new blocker, deployment, breakage found), update that row: state, blocked-on-user
items, liveness-check rows if a new surface shipped. Keep each row to one or two
lines.

## Rules

- Every parked project has a wake condition; a row without one is flagged every
  status run until it gets one or gets killed.
- Every deployed surface has a liveness check; when a project ships something
  live, add its row to the Liveness table in the same session.
- Blocked-on-user items are the briefing payload — precise and actionable
  ("choose tenancy model A/B", not "needs decisions").
- Projects live on different machines (laptop, home server, VPS, external drives).
  The board records WHERE and HOW TO CHECK, and aggregates at read time — never try
  to sync state across machines into this file.
- Update the "Last full review" line only on a full sweep, not row edits.
