# CLI changelog (https://docs.vitrinka.ai/reference/changelog)



{/* Code generated by tools/gen-docs-changelog from npm/vitrinka/CHANGELOG.md. DO NOT EDIT. Regenerate: go run ./tools/gen-docs-changelog */}

Every release of the `vitrinka` CLI, newest first, rendered from the changelog the npm package ships. Run `vitrinka update` to get the newest one. Entries before [5.0.0](#5-0-0) use the command names that release retired; its table maps each old name to the new one.

## 5.25.0 [#5-25-0]

**doc.json speaks the board's vocabulary.** A document is now a doc
element — `{"kind": "doc", "payload": {title, …, "elements": [...]}}` —
and every child is the same `{kind, payload}` a board composes: `md` for
prose, `source` for code, `cols` with keyed rows for tables, `elements`
inside sections. `vitrinka board push` rewrites a legacy `{meta, blocks}`
doc.json (and its pages) to the new shape on disk before it uploads; the
server still converts the old shape for this release and warns, and the
next release refuses it. kit-3's `Table`, `Prose`, `Code` and `Compare`
take the element names too; the old ones keep working.

**A `--json` field typo never writes twice.** Asking a write verb for a
field it does not return (`vitrinka task create … --json=id,key`) used to
fail after the task was filed, with a fix that filed it again. Once a
write has landed, a missing field is an `unknown_field` warning on the
successful answer, listing the fields that exist; before the write it is
still refused with the corrected call.

**Eve can judge a review run.** `/vitrinka:review … eve` plans a run for
Eve the way a session's own review does: Eve judges each planned screen
or crop once, skips reused and duplicate screens, and fills the same
coverage receipt.

**A board in another workspace is named, never "no work".** `vitrinka
work` on a board slug that lives in another workspace answers the
`<workspace>/<slug>` spelling that reaches it instead of reporting an
empty queue.

## 5.24.0 [#5-24-0]

**Reviews read each screen once.** `vitrinka review plan <board>…`
(`review_plan` for agents) plans a review over your boards: the latest
capture pass by default, capture gaps left out, exact duplicates judged
once, near-duplicates as a crop of what changed, and screens already
judged under the same rubric reused — on a few labelled sheets instead
of one image per screen. A host that cannot see images is stopped after
the first sheet, and the run's receipt says what was inspected, reused or
skipped. `--checklist` adds your own checks; `--full` re-judges
everything.

**Pick who judges your reviews.** `vitrinka config reviewer codex` judges
screens on the Codex sidekick, `self` (the default) in the session that
runs the review; the session start names the lane when it is not the
default.

**`vitrinka update` refreshes a slow Claude Code.** When `claude --version` (or `codex --version`) took more than three seconds — a busy
computer is enough — update printed "claude (absent)" and skipped the
plugin refresh without a word. Only a CLI missing from your PATH counts
as absent now: a slow one is refreshed, and a refresh that fails says why.

**A deleted artifact stays deleted.** `vitrinka board push` to an
artifact someone deleted is refused with `artifact_deleted` and a link to
its page, where Restore brings it back. `--revive` publishes it again on
the same page instead.

**Ticking one step keeps the rest.** A checklist written to a task
(`vitrinka task field set`, an agent's `update`) now merges by item: send
only the step you tick and the other steps and gates stay. `--replace`
writes the whole list, the way to drop, rename or reorder items.

## 5.23.0 [#5-23-0]

**Claude Code stays signed in to vitrinka.** `vitrinka update` and
`setup modules` no longer rewrite the MCP registration when the modules
it serves are unchanged — each rewrite signed every session out. Agents
can no longer start a vitrinka sign-in: that cleared the sign-in every
session on the computer shares. They now ask you to run `/mcp` →
vitrinka → Reconnect instead.

**A cut refuses a window that moved.** `vitrinka project release --cut --expect-head <sha>` refuses the cut with `release_head_mismatch`,
cutting nothing, when a refresh moved the head since you checked it. The
release prints `Page:` (and `--json` carries `docHeadSha`): whether
Eve's page was written for the current head or is stale after a refresh.
A window now starts after its start commit, so the PR that commit merged
is never listed again.

**A deploy mark says when its lineage is partial.** `vitrinka project
deploy-mark prod <sha>` also prints how many merge commits were read
from GitHub, and when GitHub's listing fell short it warns `Partial:`
with how many merged PRs still ship by time.

## 5.22.0 [#5-22-0]

**memo's memory islands reach vitrinka.** This update is all a Mac
running memo needs: memo calls four hidden doors from its outbox.
`memory snapshot put` publishes each cloud-bound island's snapshot of a
merged commit. The island's current moves only by compare-and-set, and a
stale publisher gets `cas_mismatch` with the cloud's current to adopt.
`memory usage put` sends this Mac's hourly usage tally. `memory resolve`
ranks `memo recall --deep` through Eve. `memory watch put|completions`
mirrors a `for: human` watch row to one todo and reports the todos
completed here. Agents read the cache as `get {kind: "project_memory"}`:
a bounded, ranked index, paged by cursor.

**Cut the release you deployed.** `vitrinka project release --refresh --until <sha>` sweeps only the merges that commit contains and answers
its head, so a pipeline cuts exactly the tag it shipped.
`vitrinka deploy-mark` says how History ships a production mark: by
ancestry, with how many waiting refs it contains, or by the time rule
and why.

## 5.21.0 [#5-21-0]

**A fresh session opens again from Claude Code.** `vitrinka qa session
launch <task url> <dir>` runs your `$VITRINKA_SESSION_LAUNCHER` with
both appended and prints its receipt; the brainstorm's and the hand-back's
fresh-session exits call it. They used to run the launcher through
`sh -c`, which Claude Code's removal check refuses with nobody at the
prompt, so the exit always fell back to `/continue`. Unset, not runnable
or a non-zero exit still names the `/continue` line to run by hand.

## 5.20.0 [#5-20-0]

**The plugin is four plugins.** `vitrinka` (core: publish, artifact,
brainstorming, listen, resolve), `vitrinka-pm` (pickup, handoff, tasks,
release, me — and the session hooks of the work loop, so a Claude Code
marketplace install gets them without `setup`), `vitrinka-qa` (usertest,
review, sessions, pair, spot) and the opt-in, Claude Code only
`vitrinka-experimental` (build-idea, make-idea, test, cleanup). A session
on a repo that only needs boards no longer pays for the task engine's
skills and tools. `vitrinka setup` installs core + pm + qa (`--modules`
picks, `--experimental` adds the fourth); this `vitrinka update` installs
pm and qa once on a machine set up before the split, and respects a
choice `vitrinka setup modules` has recorded. Codex keeps one bundle with
every module.

`vitrinka setup modules list|enable|disable <module>` adds or removes the
plugin that ships a module (pm, me, qa, experimental; core is always on)
and re-registers Claude Code's MCP door as `/mcp?modules=…`, so the tool
catalogue and its instructions shrink with the plugins you have.
`vitrinka setup skills show|hide <skill>` hides one optional skill
without dropping its module. `vitrinka doctor` shows a `plugin·claude`
and `plugin·codex` row per runtime and a `hooks·claude` row.

Six MCP doors the skills used to reach with raw calls: `undo` (revert
your newest batch on a board), `refresh` (re-parse a living-source card),
`import` (a truth source — openapi, sqlddl, compose, pgschema — as one
refreshable diagram), `review_verify` (link a fresh capture to a failed
judgment), `get_card_image {annotation}` and `update {kind: "session"}`.
`spot` moved to the qa module; five doors nobody called moved to `rare`.
Every skill was rewritten against the doors that exist — no `/api/v1/`
route anywhere; the ladder is the MCP tool, then the CLI, then stop.

In Claude Code type the bare `/pickup`, `/publish`, …; the qualified
`/vitrinka-pm:pickup` always works. Codex keeps `$pickup`.

## 5.19.0 [#5-19-0]

**A task says which issue it tracks.** `vitrinka task links add <id> <url>` attaches the GitHub, GitLab or Jira issue a task tracks (or a
Confluence page it refers to); `task links <id>` lists them and `task
links rm <id> <link-id>` detaches one. `vitrinka task find <url>` answers
which task tracks an issue — exactly, and `links: []` when none does.
`task create … --link <url>` files a task already linked. An issue has one
owner task: attaching it to a second answers `link_owned` with the task
that tracks it.

## 5.18.0 [#5-18-0]

**A write made offline is not lost.** When the deployment cannot be
reached — the network is down, or an agent's MCP connection dropped with
it — `task comment`, `task update`, `task create`, `task label`,
`task upload`, `task handback`, `me todo add`, `me todo done` and the new
`board compose <board> --file -` (compose\_board's arguments) queue their
request in an outbox on this machine and exit 0 with `⧗ queued offline`.
The next command that reaches the deployment replays them first, oldest
first, each exactly once. `vitrinka outbox list` shows what waits and
what the server parked, and why; `flush`, `retry` and `drop` act on them.
`--no-queue` (or `VITRINKA_NO_QUEUE=1`) keeps the old fail-fast behaviour.

## 5.17.1 [#5-17-1]

**A code-mode review shows the sheet as an image, never as text.** When
a Codex code-mode script reads `get_card_image`, the `review` skill and
the annotation docs now tell it to hand the image block to the host's
image call (`image(block)`) and print only the text block. A script that
logged the whole result would otherwise feed the image's base64 to the
model as text, which is how one review's transcript grew to 41 GB.

## 5.17.0 [#5-17-0]

**A ui-loop review keeps what did not move.** Each review batch carries a
digest per screen and a reviewer records the digests of the screens it
actually read, so retaking a few blank shots reopens only those screens,
never every finished batch. A screen whose shots did not change since
the previous pass is carried: it keeps its findings and verdicts, leaves
the review batches and counts as reviewed, and the scoreboard callout
names how many were carried.

**Several review runs share one ui-loop pass.** Each `review-loop` run
claims its own review batches, so a big pass can be reviewed by several
identical runs at once: the review stage's return says how many
(`parallel`), each of those runs continues as one, and a run that finds
every batch left already claimed ends. A run whose reviewer failed hands
its claimed batches to its own next run, which reviews them first.
Only the run that takes the pass's synthesis lease writes the backlog,
scores the pass and posts the callouts; any other ends saying another run
is synthesizing. While a capture runs, every stage waits instead of
starting beside it, and `areas` no longer narrows a review.

**A stalled reviewer no longer stalls the pass.** A review batch whose
reviewer stalls twice (the watchdog stops it) is split in halves, and the
same run reviews them with the agents it has left. A half that stalls
twice is blocked instead of retried: its screens stay unreviewed and are
named in the run's problems, the pass still gets its backlog, and the
loop then ends naming them, to judge by hand or reshoot, instead of
reviewing the pass again.

**A ui-loop polish loop can be paused.** `vybava ui-loop pause --reason
"…"` drains the whole loop: no `review-loop` stage starts (it returns
stage `paused`, a null `next` and the `resume` args to launch later),
and a running stage ends at its next boundary, after its reviewer round,
before the synthesis, between fix lanes or before Settle, keeping what
it did. `vybava ui-loop resume` lifts the pause and frees the stopped
runs' batch claims; launch the `resume` args and the loop goes on where
it stopped.

**A screen nobody could shoot is one line, not a broken finding.** A
ui-loop screen marked `retired` (dead code, a plugin not installed, a
feature off) is never shot, reviewed or counted, and `map` marks such
screens instead of calling them unreachable. A screen without a single
usable shot (unreachable, a failed recipe, an error) no longer counts
against review coverage or gets filed as a broken finding: each area
board's "Not captured this pass" card lists it once with its reason,
instead of once per viewport and theme. The fix round's rig lane
(below) repairs its recipe, and the screens a pass still has no shot of
when it reaches its final check are named in one problems line and keep
it from completing.

**A cross-lane fix is reworked, not blocked.** A ui-loop fix lane whose
fix needs a file another lane owns now defers the item with the change
it needs there, and the fix stage gives it a rework lane after the area
lanes that owns both, so it is fixed in the same round. A block now
means the fix waits on something outside the repo (seed data, a
backend, another repo, a decision), and the run that ends the fix round
returns every such item of the round in one list, `ownerQuestions`, to
ask their owner in a single batch. A skipped, blocked or deferred item
stays checkpointed while its own files are unchanged, whatever other
lanes commit; one that no longer counts is listed in the stage's
`stale`, and the next fix round takes it.

**A ui-loop fix round repairs its capture recipes.** The round ends with
one rig lane that fixes the recipes a renamed label or a dead step broke:
the literal names `vybava ui-loop check --labels` finds in no label
source, the screens the pass never shot (unless the manifest marks them
unreachable with the seed data) and those whose recipe failed; a screen
whose code is dead or whose feature is off it marks `retired`. It
reaches a control by its role and accessible name (the harness's
`action` step) instead of a CSS or `nth=` selector, checks each repair
with `vybava ui-loop run --probe` before it checkpoints it, re-renders
the app map with them, and the verify pass reshoots those screens. A
screen it cannot repair joins the round's `ownerQuestions` when its
environment blocks it, or `reworkStuck` when the repair needs the app
or the rig's project file. A pass whose review converged gets the same
repair: the loop runs the rig lane on it and then captures the screens
it repaired, instead of ending with them unshot. A label miss no longer
stops a capture, and `map` writes new recipes the same way.

**A ui-loop pass triages its failures.** After each capture, and before
the review writes the backlog, `vybava ui-loop triage` groups the shots
that failed by cause. Screens that failed on infrastructure (a network
blip, a page that stayed blank) come back as one `vybava ui-loop run --retake …` command in the stage's problems, and one application
failure behind many screens is filed as ONE broken item that names them
all, instead of a finding per screen. A recipe whose literal left the app
goes to the fix round's rig lane.
Every ui-loop stage needs Výbava v0.45.0 or later (ui-loop state
contract 10) and refuses an older one; run
`brew upgrade --cask vybava`. Update vitrinka with it: an older vitrinka
neither waits for a running capture, shares a review between runs nor
splits a stalled batch, it cannot finish a review in which a newer one
blocked a batch, it would complete a pass whose never-shot screens it
never names, and it stops a fix round in which a lane deferred an item.

## 5.16.1 [#5-16-1]

**Every survey uses the contact sheet.** The `publish` and `resolve` skills
and the experimental `test` flow now survey a set with `get_card_image {cardIds}` and measure on a single-card read, like `review`. When the server
is busy, a sheet now tells the agent to retry instead of dropping the card,
so a survey no longer silently loses a screen.

**A ui-loop stage checks its Výbava before it starts.** `review-loop`
reads the contract number `vybava ui-loop state` reports and runs
`vybava ui-loop doctor` for the stage: an older Výbava, a state field it
did not report, or a failing doctor check stops the run before anything
changes, naming the fix. Reviewers judge against the grid and touch
target your `vybava.config.ts` sets.

**A ui-loop pass knows when its shots went stale.** `vybava ui-loop
state` reports what changed since a pass was captured (application
source, the capture rig, the spec's rules), and `review-loop` runs the
stage it routes to as it is, including the exact screens a verify
reshoots, or every screen when a shared primitive changed. A stage you
asked for is never swapped for a capture: when the app drifted it stops,
lists the changed paths and hands back the capture to run, and a run
limited to `areas` never shoots. Spec rules changed since the capture
reach reviewers and fixers as final owner decisions; a screen no reviewer
could judge (a missing or blank shot) is filed as a recipe or capture
finding the fix round repairs and the verify reshoots, not recaptured.
A pass whose captured revision this clone lacks, or one shot before
passes recorded their revision, hands back a capture (or the fetch)
instead of running on shots nobody can date.
Every ui-loop stage needs Výbava v0.35.0 or later (ui-loop state
contract 2) and refuses an older one; run
`brew upgrade --cask vybava`.

## 5.16.0 [#5-16-0]

**Review a set of screens through one contact sheet.** `get_card_image`
takes `cardIds` (up to 12) and answers with one labelled sheet plus each
tile's card id, source size and version. The `review` skill surveys a board
that way on every host, Codex included, and image bytes never pass through
a shell command again: one Codex review had grown to 41 GB echoing base64
into its own commands.

## 5.15.0 [#5-15-0]

**Publish any number of screenshots.** `board push`, `board upload`,
`board dump` and `qa run publish` no longer stop at a few hundred files:
pushes upload six files at a time, uploads travel in batches with retries,
and a big run's screenshots are staged first and published by reference.
Nothing to split by hand, ever.

**Your project wears its app's icon.** `vitrinka setup` finds the icon in
the repository root (`public/apple-touch-icon.png`, `app/icon.png`,
`public/favicon.svg`, `favicon.ico`, Expo's `expo.icon`, …) and sends it
as the project's icon; an icon someone chose in vitrinka stays unless you
run `vitrinka setup --force-icon`.

## 5.14.2 [#5-14-2]

**Every test case keeps its own screenshot.** `vitrinka qa run` used to
match screenshots by file name, so a Playwright report written on another
machine (a devbox, CI) could put one `test-finished-1.png` on every card.
Paths from another machine are now found again under the report's folder,
same-named files from different tests get distinct names, and a screenshot
that can't be found is left out with one warning instead of borrowing
another test's picture.

## 5.14.1 [#5-14-1]

**Workflows resume from verified progress.** Build and review runs share a bounded
agent allowance, serialize edits, retain unfinished route and journey checks,
and tie reusable reviews to the captured source and screenshot contents.
Interrupted fixes resume before stale screenshots trigger a new capture.

## 5.14.0 [#5-14-0]

**A full UI polish pass, chained run by run.** In a repo whose
`vybava.config.ts` carries a `uiLoop` section, the `map` workflow
(`scope: "screens"`) extends the screen manifest, and `review-loop` runs
one stage per run — capture, review, fix, verify — handing back the
arguments for the next run. A pass resumes from the files it left after
any cutoff. Pass `repo` (an absolute path) to pin the run to one
checkout; a mismatch stops it before anything changes. Needs Výbava
v0.25.0 or newer.

**One board per area, every pass.** A ui-loop pass lays itself out on the
area's set board — a section per pass, viewport and theme, plus one "Not
captured this pass" card — and never creates a second board.

**A pass never calls unjudged screens clean.** An empty screen list stops
the run, the backlog records which screens were actually reviewed, and
the stages hand lists over through `vybava ui-loop` instead of an agent
summarizing them, so large passes (hundreds of screens) run end to end.

**`vitrinka update` migrates a pre-rename Codex plugin.** A machine whose
Codex marketplace still carries an old name ends up on `vitrinka@kit`
after one update, and `vitrinka doctor` names the legacy id until then.

## 5.13.0 [#5-13-0]

**Push a big take from a file.** `vitrinka board artifact push
page.html --board <slug> --section "D1 · <title>" --device phone`
lands one self-contained HTML page (up to 8 MiB) as an artifact card —
for takes too large for MCP. On a brainstorm board `--section` (the
decision's chapter) and `--device` are required; a refusal lists the
chapters to pick from.

**Brainstorms stay on the brainstorming flow.** The brainstorming skill
builds a board through the decision map and its chapters — no more raw
API takes, which a brainstorm board now refuses with the corrected call.
Each option gets its own card that pictures only that take: two options
naming one card are refused. The board owns its decisions, so a session
no longer mirrors a chapter into a separate question prompt.

## 5.12.0 [#5-12-0]

**Ask a whole project.** `vitrinka task ask --project <p> "what blocks
4.0.0?"` puts an open question to Eve, which searches the project with its
own task search and answers in prose with the tasks it found, each with its
link and one line on why. Only live tasks of that project come back, and
it spends AI credits. `task ask <id> "…"` still asks about one task.

**`vitrinka update` stops tripping on other marketplaces.** Refreshing the
Codex plugin now upgrades only the kit marketplace. A failing third-party
marketplace no longer reports the vitrinka refresh as failed.

## 5.11.1 [#5-11-1]

**The handoff no longer files a decision you could be asked.** The handoff
skill's list of steps to file on the task names a decision only when a run
nobody attends leaves it; with a human in the session the call is asked
and answered there.

**Ask for tasks in words.** `task list --ask "open stories under the
restart epic"` lets Eve compile the phrase into the filter document, prints
what it read, and pages on with that document as `--filter`; a facet flag
beside `--ask` is refused, and a server that predates it says so instead of
answering the unfiltered page. `search <ref> --resolve --deep` lets Eve
break an ambiguous verdict's tie (AI credits).

**Decisions are asked while you are there.** The handoff and spot skills
ask a call the person in the session can answer now and record the answer
in the hand-back; a `Decide:` gate is filed only by a run nobody attends (a
Workflow, a headless or scheduled run). A brainstorm decision settled in
the terminal closes its board question, and the skills' `?modules=` advice
names every module to keep (`core,pm,qa,me,rare`), so opting into `rare`
no longer hides the task and personal tools.

**Task search travels light.** `task search` reads compact rows, about
2.5× smaller on the wire, with the same columns; `task list --text` matches
tasks carrying every word while `task search` ranks any of them. `vitrinka
help json` documents the envelope as it ships (`next[].do`,
`diagnostics[].why`, `exit_code` on failure only), and the skills, help
and session hints name the current commands and MCP calls.

## 5.10.0 [#5-10-0]

**Find one task in one call.** `vitrinka search <ref> --resolve` reads a
title, any id spelling, a URL, a branch or "bugs under \<epic>" as one task
reference and answers a verdict (exact, confident, ambiguous, none) with at
most five candidates, each with its parent, child count and why it matched;
a task verb handed a name instead of an id points at that exact call.
`task search` filters by `--type`, `--state`, `--parent` and `--epic` (any
depth), `--within <task>` searches one task's tree, each row says where it
matched (`≈` marks a typo correction), and a type, state or project that
names nothing is refused with the allowed values instead of an empty list.

**Each shot uploads once.** Screenshot sets sync file by file: every
capture sends only its new shots and the board fills live as they land, an
explicit `board push` drops what the folder no longer holds, a set past 100
files no longer trips the guard, and `qa run` reuses shots the set already
holds; older deployments keep the whole-set upload. The plugin's review and
code loops now fix on their last pass, converge only when every gate was
green and walk every fixed usertest failure again; the experimental `test`
run marks a PR ready only on a record a reviewer judged.

## 5.9.0 [#5-9-0]

**Task reads are flat, and every task spelling works.** `task search` and
`task list` return flat rows with `parentId` and `url`; `--json=id,title`
works on search, list and get; `task list --parent <task>` and
`task get --include children` read an epic's tree in one call; search
stays in this checkout's project unless you pass `--all` (`--limit` sizes
it). Every task verb reads `42`, `#42`, `vt-42`, `VIT-42`, `fixit/42` or
the task URL, and a spelling it cannot read fails with the fix before any
request. `--jq` never hides a failure: a failed call prints its whole
envelope with its exit code.

**A failed request names its phase.** A DNS lookup that did not answer
(with the resolver file behind it), a server that took the connection but
never answered, or the network, instead of blanket proxy advice;
`vitrinka doctor` gains a `dns` row. Also new: `board capture safari`
shoots Mobile Safari on the iOS Simulator through Appium in six states
with the measured safe-area insets; `setup --experimental` installs the
opt-in `vitrinka-experimental` plugin (Claude Code only). `setup` and
`doctor` keep a `?modules=` MCP list, `work watch` stops with a fix on a
board this workspace does not hold, `task get` marks Eve and agent
comments, `admin smtp` names who may edit the deployment mailer, and the
tasks skill finds an epic by name in one call.

## 5.8.0 [#5-8-0]

**Release notes read like a register.** Pushed documents take the new
release vocabulary — galleries, compare charts, audience-tagged sections
and table rows (`{cells, audience}`), a unit per compare item — and export
from one viewer as PDF, HTML, email or Word. `vitrinka project release --notes <ref>` links a release to its notes document (`--notes-clear`
unlinks it), and the release pool ranks screenshots by visual score so the
notes carry the best shots once each. Web captures are 2× device scale by
default (`capture.hidpi` in `vitrinka.config.json`). Slow uploads fail on
an idle stall instead of a fixed deadline (`VITRINKA_UPLOAD_IDLE`
overrides it), rapid captures on a slow link share one background push
per capture folder that always ends on the newest shots, and
`review file` without a valid `--agent` names the accepted runtimes and
the corrected call.

## 5.7.1 [#5-7-1]

**Errors say what the server said.** When the server answers a 502/503/504
with its own problem (a release sweep that cannot read a repository, for
example), the CLI now shows that reason and fix instead of claiming the
deployment is not answering; a real gateway outage still reads as one.
Composed document cards on boards now size to their content instead of
arriving clipped.

## 5.7.0 [#5-7-0]

**Releases span every repository of a project.** A new release track sweeps
each repository bound to the project, each on its own branch and from its
own start, and stores both on the release — later refreshes and the next
window after a cut need no flags. PR links name their repository, so two
repositories' `#123` never mix. Drive it with the new
`vitrinka project release` (`--refresh --repo <owner>/<name>:<branch>@<start>`,
`--cut`); the release skill documents the fork-point rule for choosing a
start on an integration branch, the page's real shape and the
`X-Vitrinka-Workspace` header for raw API calls. Prose links in documents
are underlined like table links.

## 5.6.2 [#5-6-2]

**Links are first-class in authored documents.** Every section is an
anchor (`id` authored or derived from its label), so `/a/<card>#<id>`
deep-links into a document and `{kind:"ref", section}` cross-references it
in place. Table cells take `{v, href, tone}` and render inline markdown like
prose. `meta.sources` declares a repository once and `alias:path#L3-L9`
works as any link target: the push expands it to the canonical GitHub URL
and, where the deployment holds a GitHub credential, verifies the path
exists at that ref. The artifact skill carries the new rules.

## 5.6.1 [#5-6-1]

**`/vitrinka:review` files findings and stops.** A review pass, local or
Eve's, ends with staged annotations on the board and a report grouped by
root cause; it no longer fixes code or composes a next pass on its own. The
user accepts or dismisses on the board, and `/vitrinka:resolve` works what
was accepted — including re-capturing as a new pass and linking the Eve
post-fix audit.

## 5.6.0 [#5-6-0]

**A native work hub without marking your project briefs read.**
`vitrinka me hub --json` gathers projects, tasks waiting on you, active runs
and linked completion, verification, merge and deployment evidence. History
covers up to 30 days, with resumable pages and explicit source coverage.

**Live updates over one read-only connection.** `vitrinka me hub --watch --json` streams snapshots when workspace activity changes, without idle
polling or claiming an annotation listener. Successes and failures each use
one JSON envelope per line, suitable for native clients such as Switcheroo.

**Automatic achievements grounded in recorded outcomes.** `vitrinka me hub --generate --json` requests daily-capped Eve proposals and reuses cached
definitions. Starter milestones and cached results remain explicit when AI
is unavailable; proposals never declare work complete. These commands need
the matching work-hub server endpoints and a personal Vitrinka identity.

**Capture details survive publishing.** Usertest snapshots retain their
route, state, device and capture context on QA cards. Capture writes also
restore the runtime directory's Git ignore file, keeping local screenshots
out of accidental commits.

## 5.5.1 [#5-5-1]

**The 5.5.0 hook ceilings actually arrive.** `vitrinka setup` compared a
hook's command but never its timeout, so a row whose command never changed
kept the ceiling it was first written with — the Stop gate stayed at 20 s on
every upgraded machine while setup reported done and `doctor` agreed. The
timeout is now part of the hook's identity: setup rewrites a stale ceiling
in place and counts it as drift. Re-run `vitrinka setup` once on 5.5.1.

**`setup` repairs the Codex hooks it is told to repair.** `doctor`'s
`hooks·codex` row has always named `vitrinka setup` as its fix, but setup
only ever touched Claude's `settings.json`, so a machine kept the retired
`hook-context task` row in `~/.codex/hooks.json` indefinitely. The step now
covers every harness present, and the presence probe for the Claude CLI is
memoized like its Codex twin — setup asked for it up to three times, each a
3-second subprocess.

## 5.5.0 [#5-5-0]

**Breaking: a hand-back files STEPS on the task, not a pile of child
tasks.** `hand_back` / `vitrinka task handback` lands each `next` item as a
step on the task's own checklist; a child task appears only when the item
says `task: true` with a type, so an agent has to argue for a task and
never for a step. New flags: `--step "name[::kind][::who-or-date][::when]"`
and `--gate "name[::who][::when]"`; `--next` now means a child TASK. An
`omitted` item starting with `Decide:` becomes a human gate that blocks
Done. A step whose trigger is observable carries `when` in the todo grammar
(`pr 12 merged`) and the server ticks it with evidence. A pickup leads with
the task's open steps, then its children. ⚠️ An older `@vitrinka/cli`
sending a raw `next` item as `{title, type}` with no `task` key now gets a
422 telling it to set `task: true` — update before handing back.

**The session-end auto-archive is gone.** `vitrinka qa session archive
on|off|status` and the `SessionEnd` hook that uploaded a whole redacted
transcript whenever a session naming a task ended no longer exist — every
such session left a record nobody asked for, and the record buried the
hand-back. Archiving a transcript is an explicit hand-off act:
`vitrinka qa session archive <transcript.jsonl> --task <id>` when the work
passes to a colleague, with the task labelled `eve-distill` so they open
the digest. `vitrinka setup` strips the retired hook.

**One SessionStart hook instead of three.** `vitrinka hook-context start`
prints the run stamp with the pickup, the todo cue and the schedule cue in
one process and one envelope; the cues fire only on a real start (Claude
Code's `source` is `startup` or `clear`), so a resume or a compaction pays
the stamp alone and needs no matcher. Timeouts are short (8 s start, 10 s
task-end, 5 s stop, 5 s touch) and the Stop gate reads only the last 4 MiB
of a transcript. `vitrinka setup` replaces the old `task`, `todo` and
`schedule` rows in place; the old verbs still answer for machines that have
not re-run setup.

**NEXT is a work queue, not a menu.** The pickup skill works the task's open
steps and then every child down the list until only rows a human must do
remain — a decision the tree waits on is asked mid-flight, never filed and
left; only the context window ends a sitting. Its mirror in the handoff
skill: `next` holds only what the human must do, and work the agent can do
is built before the hand-back.

**Smaller:** the listen skill's ladder is served by the server
(`docs {topic: "…"}`) instead of shipping as a file in the plugin, and the
Claude plugin renders into its own kit subtree so an installed plugin cache
holds nothing installable.

## 5.4.0 [#5-4-0]

**Breaking: the old `<verb>_<kind>` tool names are gone.** `list_tasks`,
`get_task`, `create_board`, `get_brief`, `list_work`, `get_session` and every
other name the six generic verbs absorbed in 5.3.0 no longer answer; the call
is `list · get · create · update · delete · search {kind: "<kind>", …}` —
the kind is the old name's tail, singular for list and search (`list_tasks`
→ `list {kind: "task"}`, `get_brief` → `get {kind: "brief"}`, `list_work` →
`list {kind: "work"}`). An old name answers a one-line rename hint, nothing
else. Per-kind contracts: `docs {topic: "kind:<kind>"}`. `update_cards`,
`get_card_image`, `get_templates` and `search` keep their names.

**The catalogue lost weight.** Argument prose moved out of `tools/list` into
docs topics, so a registration pays for the tools it can call, not for their
manuals. A new `rare` module holds the doors a session almost never needs
(`transfer_project`, `merge_project`, `mark_deploy`, `promote_meeting`,
`rename_set`, `review_judge*`, `review_stats`, `review_job`,
`meeting_notes`, `save_template`, `dry_run_rule`, `edit_journey_node`,
`set_preferences`, `favorite`, `unfavorite`, `my_favorites`,
`retract_questions`, `board_working`, `request_review`) — never listed by
default; opt in on the registration's `/mcp` URL with
`?modules=core,pm,qa,me,rare`. `usage` joins `select`, `jq` and `budget` as
a universal argument. `vitrinka doctor` prints the tool count the server
advertises to this registration.

**A bare task id is refused where it is ambiguous.** On an organisation-wide
grant that spans more than one workspace, an id-only tool on the root
`/mcp` door no longer guesses: a bare `{id: 42}` answers `spell it <workspace>/<id>`. A `project` argument that pins the workspace, the
`/w/<slug>/mcp` door and the `<workspace>/<id>` spelling all work as before.

The contract date moves to 2026-09-15 — served by `/api/v1/version`, stamped
in every skill and in the kit export. Every skill now spells the generic
verbs, and the ones that lean on a `rare` tool say so.

## 5.3.0 [#5-3-0]

**Skills went on a token diet; the MCP refuses to dump.** Every MCP tool
takes `select`, `jq` and `budget`; a reply above the budget (8k tokens by
default, 30k ceiling) comes back as an `over_budget` envelope naming the
tool's narrowing arguments instead of the dump. The 52 `<verb>_<kind>` tools
collapsed behind six generic verbs (`list · get · create · update · delete ·
search {kind}`; the old names still answer, flagged deprecated), `tools/list`
is scoped to the project's modules, and PM reads ride compact rows. The
plugin ships 15 skills instead of 21: the five personal skills are ONE
manual-only `me` skill, `answers` and `annotations` dissolved into the MCP
instructions, and every SKILL.md is a trigger plus contracts.

**Procedure moved to the server.** The step choreography the publish,
brainstorming and sessions skills used to carry is served as guide pages:
`vitrinka docs guide:publish-capture`, `guide:brainstorm-flow`,
`guide:sessions-pipeline` (MCP `docs {topic: "guide:<name>"}`, index
`docs {family: "guide"}`). A wording fix is a deploy, never a plugin update.

`--json` envelopes of `task pickup`, `task start`, `task spot`, `task
handback`, `qa run` and `qa usertest start · case · verdict` now carry
`next[]` — the command to run next, executable as printed. `vitrinka doctor`
reports MCP replies that ran over budget.

MCP: `set_board_meta {archived: true}` retires a board (false restores it);
a task `id` spelled as a numeric string (`"289"`) is accepted like the
number; the `spot` and `hand_back` receipts name the parent task as a line
instead of embedding its whole row.

`vitrinka project merge <from> --into <to>` merges a project into another
project of the same workspace: everything \<from> owns moves — tasks with
their ledger, comments and attachments, boards, sets, sessions, reports,
releases, press editions and project memory — nothing is dropped (a clashing
custom field is renamed `<key>_from_<from>`, a clashing set `<key>-from-<from>`,
values kept), and the old
name stays as an alias so every old link keeps working. Dry run by default;
`--apply` writes, `--adopt --title "<epic>"` parents the moved root tasks
under one new epic. `project merge-plan --into <to>` lists the strays a
merge would sweep. `project fold` / `fold-plan` keep working as hidden
aliases; `--adopt "<title>"` became `--adopt --title "<title>"`.

`vitrinka task update <id> --project <slug>` moves one task with its whole
subtree into another project; states, labels, fields, sprint and milestone
map by key and missing keys are created in the target.

## 5.2.0 [#5-2-0]

**One sign-in ever on Claude Code.** Claude Code keeps a separate OAuth token
per MCP entry, so the project-scope `.mcp.json` entry cost a sign-in in every
repo. `vitrinka setup` now registers the root `/mcp` door at user scope only
and removes a leftover project entry (commit the change); `vitrinka doctor`
names one; the SessionStart hook tells the session which workspace the repo
is bound to and how to address another (`<workspace>/<project>`). The server
resolves a named project to the workspace that knows it — the grant's home
first, then the single other workspace of the organisation that has it.
Cursor, OpenCode, VS Code and Gemini CLI keep their committed files.

`--local` no longer registers anything for Claude Code (its registry is
global, like Codex's).

## 5.1.1 [#5-1-1]

`vitrinka setup` actually performs the 5.1.0 layout migration. In 5.1.0 it
reported success and changed nothing on any machine whose plugin and MCP were
already registered — which is every machine with a repo old enough to need
migrating. Run `vitrinka setup` once in each bound repo and commit what it
prints: the fold into `vitrinka.config.json`, the untracked manifests and the
pruned `.gitignore`. An ignore line for a SIBLING path like `.vitrinka-verify/`
is left alone; only the runtime dir's own lines go.

## 5.1.0 [#5-1-0]

**One MCP sign-in per origin, and `vitrinka mcp` is gone.** The OAuth grant
now covers every workspace of your organisation: consent picks a home
workspace, and every registration names the same `/mcp` endpoint, so
switching repos never re-authenticates and parallel sessions in different
workspaces no longer knock each other out. A repo's workspace rides an
`X-Vitrinka-Workspace` header in its committed entry (`.mcp.json` spells the
origin `${VITRINKA_URL:-https://app.vitrinka.ai}` so self-hosted clones
follow one exported variable); to act in another workspace of the
organisation, name the project `<workspace>/<project>` — in MCP calls and in
`--project`. Codex registers the same door (`codex mcp login vitrinka`).

`--mcp-stdio` no longer exists.

**One committed vitrinka file per repo.** The workspace binding, project slug,
runners and run stacks now live at the top of the root `vitrinka.config.json`,
beside the index policy. `.vitrinka/` is a machine-local runtime directory
that ignores itself, so your `.gitignore` needs no vitrinka lines at all — and
`journeys.json` / `sessions.json` are no longer committed (the task engine is
their record). The old `.vitrinka/project.json` is still read for this
release; `doctor` names it until it is gone.

**Run `vitrinka setup` once after updating.** It folds
`.vitrinka/project.json` into the root file, untracks the old manifests,
removes every `.vitrinka` line from `.gitignore` and `.git/info/exclude`, and
rewrites stdio-forwarder and `/w/<workspace>/mcp` entries onto the one `/mcp`
door. `vitrinka doctor` names a leftover forwarder as a dead door.

## 5.0.2 [#5-0-2]

`vitrinka setup` now installs the todo hooks it always claimed to. `doctor`
names setup as the repair when the SessionStart todo rows are missing their
`startup|clear` matcher or the PreToolUse touch row is absent, but setup only
ever wrote the task-run hooks — it printed "Claude hooks done" and changed
nothing. Run `vitrinka setup` once after updating and `vitrinka doctor` goes
clean.

Until a machine does that, its session starts still inject the whole open
todo list on every start, resume and compaction, instead of the handful of
todos whose condition actually fired.

## 5.0.1 [#5-0-1]

`vitrinka update` and `vitrinka doctor` are back at the top level, the way
`brew doctor`, `flutter doctor` and `<tool> update` read; `setup update` and
`setup doctor` are gone (unknown command, exit 2 — no aliases). `setup` keeps
the bare first run plus `uninstall`, `extension` and `skills`.

`vitrinka task list --json id,title` (a space before the field list) used to
fail as "unknown command id,title". It now says what it means: the field list
rides the flag with `=` — `--json=id,title` — and the fix line is the
corrected command.

## 5.0.0 [#5-0-0]

One vocabulary. The CLI is twenty entries: nouns that group verbs, three
leaf verbs (status, help, completion), nothing else. Old names are gone —
they print "unknown command" and exit 2. There are no aliases.

```text
Old                                New
install · project setup            setup            (the whole first run)
doctor · extension doctor          doctor
update · uninstall                 update · setup uninstall
extension … · skills …             setup extension … · setup skills …
set init|push|capture|rename|meta  board init|push|capture|rename|meta
artifact … · diagram …             board artifact … · board diagram …
open · edit                        board open · board edit
run · usertest · pair · session    qa run · qa usertest · qa pair · qa session
todo · diary · brief · time        me todo · me diary · me brief · me time
schedule <title> --in …            me schedule add <title> --in …
schedule list|ripe|move|skip|end   me schedule …
listen                             work listen
resolve <url> · query <sql>        search resolve <url> · search query <sql>
ai <words>                         docs ask <words>
tidy                               project tidy
ui                                 (bare `vitrinka` on a terminal)
login · logout · token             auth login · auth logout · auth token
push · snap · remote-init          board push · board capture · board init
name · meta · upload               board rename · board meta · board upload
board-from-set · journey-from-set  board create · board create --journey
artifact-init · artifact-from-set  board artifact init · board artifact create
watch · setup-project · index      work watch · setup · project index
import · schema                    board diagram import · board diagram schema
operator · use · smtp              config operator · config bind · admin smtp
```

### First run [#5-0-0-first-run]

* **`vitrinka setup`** is the one door: sign in, agent plugin + MCP, detect
  this repository, review the proposed index (all tracked supported files
  minus lockfiles, generated, vendored and `.github`), apply, next action.
  Done steps are skipped on rerun; Back keeps your answers; the key bar
  survives 80 columns. `--advanced` opens the directory picker.
* **`vitrinka doctor`** checks the installation and this repository;
  `--all` adds every registered project. `--fix` shows a target · before ·
  after plan and applies it after review.

### Agents [#5-0-0-agents]

* `--no-input` never prompts (also `VITRINKA_NO_INPUT=1`, `CI=true`);
  `--yes` accepts the proposed plan; a step that needs a person exits 5
  `needs_human` with the plan in the envelope and the `--yes` rerun in
  `next[]`.
* `--json=id,title` prunes `data` to those fields; `--jq '<expr>'` filters
  the envelope.
* Exit codes: 0 · 1 · 2 misuse · 3 nothing found · 4 sign in · 5 needs a
  human · 130 cancelled.
* `vitrinka help exit-codes|json|no-input|environment|agents` explain each
  contract; every command's help carries the same vocabulary.

## 4.2.0 [#4-2-0]

`update` can no longer skip a plugin silently, `doctor` catches a plugin that
fell behind the CLI, board reviews become audited judgments, and work time is
measured instead of typed.

### Fixed [#4-2-0-fixed]

* **`vitrinka update` refreshes the Codex plugin again.** When the
  `codex plugin list` probe failed, the runtime printed as `(no plugin)` and
  the refresh was skipped without a word — Codex sat two releases behind
  while `update` reported success. A failed probe now prints
  `codex (probe failed)`, warns with the real failure and lands under
  "steps need a hand" with the re-run; a probe whose process exited is
  taken as its answer even when a grandchild held the pipe past the deadline.
* **`vitrinka doctor` reports plugin version drift**: an installed plugin
  whose version disagrees with the CLI shows
  `stale — 3.11.0 installed, CLI is 4.2.0 — repair: vitrinka update`
  instead of a green `installed`; a failed probe is a red `probe failed`
  row, never `not installed`.

### Added [#4-2-0-added]

* **`vitrinka review judge <board|set>`** replaces the old board reviewer:
  captured screens are judged server-side, every failed judgment gets an
  independent audit, and `--status` reads compact results. `review judge
  export <job>` prints version-pinned judgments for local triage and
  `review file` files the ones you keep. Skills `review` and `tasks` follow.
* **`vitrinka time enable|disable|status|capture`*&#x2A; and &#x2A;*`time service
  install`** — opt-in, automatic capture of human and agent effort
  (Claude/Codex hooks, a local collector kept running at login) feeding the
  task and epic time views;
  `get_work_time` / `get_work_evidence` / `get_work_forecasts` read the
  ledger over MCP.

## 4.1.0 [#4-1-0]

The brainstorm hands itself off: a feature brief the next session reads
instead of the whole board, one worktree per feature, and a machine ledger
`doctor` can repair.

### Added [#4-1-0-added]

* **`vitrinka task brief <epic> [--out <file>]`** composes the brainstorm
  board's `Plan` chapter (chosen takes only) and prints the brief skeleton;
  the skill fills the prose and re-uploads it as the next version.
* **`vitrinka task upload <id> <files…> --kind <kind> --hint <text> --version-of <refId>`**
  attaches typed, hinted files, optionally as the next version of an
  existing lineage.
* **`vitrinka task pickup` honours `meta.load`** on refs: `first` leads the
  READ rail, `on-demand` prints as a `read on demand:` line, `never` hides.
* **Brainstorming skill**: `deep` level (same flow, bigger budget), spike /
  bounded / architectural classification before the first question, the
  feature worktree created at step 1, a vocabulary challenge, and every
  brainstorm closes with "in which style to implement".
* **`project setup` picks the project*&#x2A; from the workspace's projects and
  their declared repositories; &#x2A;*`project link` / `project unlink`** manage
  a checkout's binding.
* **`vitrinka doctor` reads the machine ledger** (`projects.<name>.repos[]&#x60;
  in the config) and &#x2A;*`doctor --fix`** repairs machine, server and per-repo
  drift after one confirm.
* **`vitrinka dev` seeds a personal workspace** beside the team one.

### Changed [#4-1-0-changed]

* `project setup` appends the origin to a non-empty declared repository
  list instead of leaving it alone.
* The repo-side `docs` guide gains a `brief` topic (`vitrinka docs brief`).

## 4.0.0 [#4-0-0]

One `--json` shape for ordinary commands, including help, version and doctor.
An agent reads `ok`, then `data` or `diagnostics`, then `next`.

### Changed (breaking) [#4-0-0-changed-breaking]

* **`--json` is an envelope**: ordinary commands print
  `{v: 1, ok, verb, data, diagnostics, next}` on stdout, success and failure
  alike. What a command printed before is now its `data`; a failure carries
  one or more `diagnostics` (`code` · `severity` · `detail` · `why` · `fix`
  · `http_status`) plus `exit_code`, and `next` lists `{do, cmd}` steps.
  Scripts reading a top-level field (`.url`, `.slug`, `.error`) read
  `.data.url` / `.diagnostics[0].detail` now.
* **Misuse is a coded diagnostic, exit 2** — an unknown command, a wrong
  argument count or a bad flag value answers `unknown_command` /
  `bad_argument` with the corrected invocation in `fix`. An unknown
  subcommand no longer prints the group's help page and exits 0.
* **Every failure carries a `code`** from one closed vocabulary shared with
  the server (`not_signed_in`, `unreachable`, `not_found`,
  `gateway_unavailable`, …); an error with no better class is `failed`.
  A raw library error never reaches you as styled prose under `--json`.

### Added [#4-0-0-added]

* A bare group under `--json` (`vitrinka task --json`) lists its commands
  as `data.commands` instead of printing help.
* The server's own error `code`, `detail` and `fix` are read into the
  diagnostic when the reply carries them.
* REST errors, including authentication and routing failures, carry a code
  and recovery instructions. MCP exposes the same object in `structuredContent`.
* `doctor --json` emits one failure with check results. Permission failures
  do not suggest repeated login; malformed JSON bodies cannot silently mutate data.
* Protocol/stream exceptions retain their output: MCP, extension host,
  child commands via `run`, `listen`, continuous `work watch`, raw `auth token`,
  completion scripts and interactive UI.
* Go source builds use module `github.com/henderson-tech/vitrinka`; update
  custom import paths and `-ldflags -X` references when building from source.

## 3.11.0 [#3-11-0]

The task is the handoff: a session picks a task up from one bounded view,
files what it spots the moment it sees it, and hands back through one door
whose printed block is the hand-back. Every AI coding harness can listen.

### Added [#3-11-0-added]

* **`vitrinka task pickup [id|url]`** — the server-composed pickup: what the
  task is about, the last hand-back, the branch (and whether it exists on
  origin), what to read first, who is on it, the open children in rank order
  and what was deliberately not loaded. MCP: `get_task {view: "pickup"}`.
* **`vitrinka task handback [id]`** — one atomic door: next steps and
  omissions become ranked children, the summary becomes the next version of
  the task's `handoff` attachment, PR/board refs and status land, the parent
  epic's `next_action` moves, and the printed block is the hand-back to paste
  verbatim. `-` reads the exact REST body from stdin. MCP: `hand_back`.
* **`vitrinka task spot <title>`** — file a defect or follow-up under the
  bound task (else the project's rolling *Found during implementation* epic)
  the moment you see it; `--production` adds the might-affect-production
  label. MCP: `spot`.
* **`vitrinka task start`** with no id binds the checkout's `vt-<id>` branch.
* **Hooks**: `vitrinka project setup` offers the three Claude Code hooks
  interactively and shows the exact `settings.json` entries — SessionStart
  prints the pickup, SessionEnd writes the git residue into the run, and a
  Stop gate asks for `hand_back` on a hand-back turn. `vitrinka install`
  keeps registering them.
* Two built-in rules move status by themselves: a run starting marks the
  task in progress, an opened PR marks it in review (`merged-pr-completes`
  stays).
* **`vitrinka listen --harness <name>`*&#x2A; — host any harness (Codex, Gemini,
  OpenCode, Cursor, …) over the Agent Client Protocol and feed it the scope's
  board work; &#x2A;*`work watch --exec <harness>`** is the headless resume rung.
  `install` provisions the adapters behind consent; `doctor` prints the
  `listen·` and `acp·` rows.
* **`vitrinka search <text>`*&#x2A; — the ⌘K palette from the terminal: tasks,
  pages, boards, tags, cards, sessions and shots in one call; &#x2A;*`vitrinka
  resolve <url>`** names what a pasted app URL points at, in any workspace.
* **`vitrinka project fold <name> --into <home>`** (`fold-plan` for the dry
  run) folds a stray project name into its real home and leaves an alias;
  `project transfer` retires the source name.
* Skills: `pickup`, `spot` and `handoff` (module `pm`); `listen` carries the
  ONE listening ladder every finishing skill points at.

### Changed [#3-11-0-changed]

* A task or epic URL in a prompt means "pickup first" for every MCP client;
  the full tree is never loaded up front.
* Task deep links are `/p/<project>/t/<id>`; the `/t/<id>` short form is gone.

## 3.10.0 [#3-10-0]

One verb per testing lane: run your suite, or explore by hand, and vitrinka
keeps the QA record — task, journeys, verdicts, board, screenshots, bugs.

### Added [#3-10-0-added]

* **`vitrinka run -- <test command>`** — runs the command with the run env set
  (`VITRINKA_RUN_ID`, `VITRINKA_RUN_DIR`, `VITRINKA_RUN_STARTED_AT`,
  `VITRINKA_SHOTS=always`), finds the results it left (JUnit XML, a Playwright
  JSON report, `allure-results/`, wdio reports or a run manifest; declared
  runners first, else a scan since the start) and publishes them. Exits with
  the command's exit code; `--dry-run` prints the folded manifest.
* **`vitrinka usertest start · case · verdict · finish`** — an exploratory
  session over `snap`: every capture attaches to the open case, `finish`
  publishes one journey per case with its verdict and shots.
* **`vitrinka project setup --runners [--check]`** — declares where each
  detected runner (Playwright · wdio · Jest · Vitest) writes results in
  `.vitrinka/project.json`; `--check` exits 1 on drift.
* A run with no task in context lands under the project's rolling
  **Unplanned runs** epic instead of failing; `run publish` does the same.
* The run door accepts JUnit XML and Playwright JSON bodies directly
  (`POST /tasks/{id}/run`, `POST /projects/{project}/run`).

### Changed [#3-10-0-changed]

* The `usertest` skill is the two verbs; board mechanics left it. The
  publisher agent keeps only walkthrough boards.

## 3.9.1 [#3-9-1]

The feature-lifecycle follow-ups: the QA board and final artifact from the
terminal, and transcripts folded once, on the server.

### Added [#3-9-1-added]

* **`vitrinka task qa-board <id>`*&#x2A; and &#x2A;*`task final <id>`** (`--json` prints
  the door reply verbatim) — the qa task's board with its living journeys
  diagram, and an epic's final board + versioned final page.
* `GET /tasks/{id}/refs/{ref}/transcript` — one server-side transcript
  normalizer feeds the task panel's viewer and Eve's corpus; the upload door
  stamps the index from it.

### Changed [#3-9-1-changed]

* `eve-summarize`'s quiet window is a persisted row swept by the runner, so a
  restart no longer drops it; the daily cap is claimed atomically.
* The final board's Delivery section lists the releases that shipped an
  epic's PRs, matched by repository and PR number.
* `session continue` reads the whole attachment (`full=1`) and writes the
  resume file into a private temp directory.
* Protocol doc §4.17–4.20 aligned with the eve-ai-layer implementation
  (`derivedFrom {id, version}`, pm-ask `scope`/`tree`, 415 on binary refs).

## 3.9.0 [#3-9-0]

Tasks become the feature record: typed, versioned attachments an agent loads
progressively, Eve on tasks through `eve-*` labels and `ask_task`, coding-agent
session archives with a portable continue, a QA plan of `journey` tasks drawn
as a living diagram, and commits that link themselves through a trailer.

### Added [#3-9-0-added]

* **`vitrinka task label <id> --add --remove`*&#x2A; and &#x2A;*`task link <id> <to> --rel blocks|relates|duplicates|supersedes|custom`**; `task update --label`
  now reaches the labels door (it was silently ignored).
* **`vitrinka session archive on|off|status|<path> --task <id>`** — opt-in
  archive of Claude Code / Codex transcripts behind a task: a secret gate
  (credentials refuse, PII masks), `//vitrinka:repo:<remote>/<rel>` portable
  paths, a `SessionEnd` hook installed by `vitrinka install&#x60;; &#x2A;*`session
  continue <task>`** prints the latest Eve distill as a brief and offers
  native resume when the harness matches.
* **`vitrinka commit-msg`** (hidden, the per-repo `prepare-commit-msg` hook
  `project setup` installs) appends `Vitrinka-Task: vt-<id>&#x60; from the branch;
  &#x2A;*`project commits --subject on|off`*&#x2A; and &#x2A;*`workspace commit-prefix
  bracket|bare|colon`** control the optional subject prefix.
* **`vitrinka task resolve-qa [--task <id>]`** — the qa task a usertest board
  or session belongs to, from the branch's `vt-<id>` or an epic.
* MCP: `label_task`, `get_task {include}`, `read_task_ref` (raw under budget,
  Eve-distilled above it or with a `question`), `ask_task {question, intent,
  scope}`, `upload_task_file {kind, hint, versionOf}`, `qa_board`,
  `edit_journey_node`, `compose_final`, `propose_tasks` drafts with `key`,
  `parentKey`, `fields`; task types `qa` and `journey`; ref kinds
  `transcript · conversation · brainstorm · plan · handoff · decision ·
  distill · commit · final`; every enum is generated from the server.
* Skills: `tasks` (planning an epic, the feature lifecycle, asking Eve),
  `sessions` (archive + continue), `usertest`/`publish` (auto-link to the qa
  task), `brainstorming` (files the feature epic).

### Changed [#3-9-0-changed]

* Unknown MCP arguments are refused with a 400 naming the accepted set (they
  were dropped silently) and recorded on the agent-calls ledger.
* `type` on `create_task`/`update_task` is documented; `todo` appears in every
  type enum.

## 3.8.1 [#3-8-1]

The public-beta surface: vitrinka is an authenticated service at
`app.vitrinka.ai` — sign up, run `vitrinka auth login`, and every skill,
message and manifest now says so. Author and repository moved to Henderson
Tech (`github.com/henderson-tech/vitrinka-kit`).

### Removed [#3-8-1-removed]

* **`vitrinka import pultik`** — the one-off Obsidian vault importer is gone;
  `vitrinka import <file>` still forwards to `diagram import`.

### Changed [#3-8-1-changed]

* Unreachable-host and `doctor` messages point at your network or
  `VITRINKA_URL` for self-hosted deployments instead of a VPN.
* Skills and MCP tool descriptions describe the authenticated service; Eve is
  introduced as vitrinka's AI reviewer; example slugs are `acme`.
* The npm launcher and platform packages are Elastic-2.0 and ship the LICENSE;
  author Henderson Tech; issues and README link to the public kit repository.
* Docs: the quickstart links sign-up; the report widget page describes the
  served `report-widget-1.mjs` script.

## 3.8.0 [#3-8-0]

Personal todos and scheduled reminders live in vitrinka: a `todo` task type
with the moment preset (trigger · lead · every · context · commit · branch),
milestones that can be reached, and ripeness computed when you ask.

### Added [#3-8-0-added]

* **`vitrinka todo add|list|show|done|drop`** and `todo milestone
  add|list|reach|unreach` — the todo engine from the terminal; `done` rolls
  a recurring todo forward on your wall clock and keeps it open.
* **`vitrinka schedule <title> --in|--at [--lead --every]`** with
  `list|ripe|move|skip|end` — timed reminders; `ripe --claim` announces once
  per cooldown through a per-machine, fail-open ledger.
* **`vitrinka hook-context todo|schedule`** — the Claude Code SessionStart
  context (open todos of this checkout's project; ripe reminders across
  projects). Prints nothing rather than failing.
* **`vitrinka import pultik [<vault>] [--apply]`** — imports an Obsidian todo
  vault: dry-run by default, idempotent by title, milestones per project,
  `depends_on` as `blocks` links.
* **Skills `/todo`, `/todo-list`, `/todo-done`, `/todo-milestone`, `/remind`**
  under the new `me` module (`vitrinka skills` lists core · pm · qa · me).
* MCP: `ripe_todos`, `complete_todo`; `update_milestone {reached}`;
  `todo add --body-file -` reads a body from stdin.

### Changed [#3-8-0-changed]

* `vitrinka import <file>` (the legacy diagram import) now lives under the
  `import` group and forwards unchanged.
* `task list --type` accepts `todo`; `fields=summary` rows carry longtext
  fields as excerpts and an exhausted page omits `nextCursor`.

## 3.7.1 [#3-7-1]

### Fixed [#3-7-1-fixed]

* **`vitrinka update` migrates a Codex marketplace registered under a legacy
  name.** Codex refuses `marketplace upgrade` for a clone registered as
  `lovinka` or `vitrinka` once the kit's manifest names it `kit`; update now
  drops the stale registration, re-adds the kit and re-adds the plugin
  instead of handing you the error.

## 3.7.0 [#3-7-0]

One version, everywhere: the `vitrinka` agent plugin (Claude Code / Codex
marketplace `vitrinka@kit`) now carries the CLI's version, so `vitrinka
update` always brings the skills and the MCP vocabulary that match this
binary. The plugin jumps from 1.11.0 to 3.7.0 in one step.

### Added [#3-7-0-added]

* **File a task from a board card.** Right-click any card → *File a task
  from this card*: the task lands in the project with the card as its
  evidence, and (when you may accept intake) is projected straight back
  onto the board as a task card. Cards wear a quiet `REN-12 · done ↗` link
  to the tasks filed from them.
* **Pin a task on a board** from the task panel's menu; task cards link
  back to their task.
* **`docs {family: element|board|artifact}`** narrows the docs index to one
  layer (about a third of the tokens).

### Changed [#3-7-0-changed]

* `scrape_board` and `list_sections` take `board` like every other board
  tool (`slug` still works).
* `list_tasks` answers compact rows — label names, no bodies, no actor
  objects, no timestamps; `get_task` has the full record.
* The docs index shows one sentence per topic.

## 3.6.0 [#3-6-0]

### Added [#3-6-0-added]

* **`vitrinka docs [topic]`** — the connected deploy's artifact component
  reference, in the terminal. No topic prints the compact index; a topic
  prints that page (props, example, gotchas). It reads the docs off the
  server you're signed into, so what prints always matches the kit that will
  actually render your artifact — even one newer than this CLI.
* **`push` preflight autofixes doc.json.** Mechanical mistakes — typos,
  HTML entities, small JSON damage — are repaired before validation and
  reported as `fixed:` lines; `--no-fix` skips the repair. Validation
  failures and server rejections now come back as structured diagnostics
  naming the exact spot (carried in the `--json` envelope too) instead of a
  bare error.
* **Artifact pushes come back with a screenshot.** After publishing, the
  server smoke-renders the artifact and the CLI downloads `render.png`
  beside it, so you see what shipped without opening a browser.
  `--no-render` skips the wait and the download.
* **Your repo's `DESIGN.md` rides along.** Artifact pushes discover the
  repo's DESIGN.md and send it so renders follow your design language;
  `--no-design` opts out. `artifact init` quotes the style brief into new
  scaffolds.

### Changed [#3-6-0-changed]

* **`artifact init` emits ONE scaffold** — an editable index.html composing
  kit components, ready to rework directly. The `--custom` full-HTML escape
  hatch is retired; the command points you at composing kit components
  instead (see `vitrinka docs`).
* **The project slug follows the repo binding from any subdirectory** — a
  nested working directory now honors the nearest ancestor
  `.vitrinka/project.json` instead of resolving as if the repo were unbound.

### Fixed [#3-6-0-fixed]

* An oversized or garbage server response now fails loudly, naming what was
  wrong with it, instead of being silently truncated into confusing output.

## 3.5.0 [#3-5-0]

### Added [#3-5-0-added]

* **`vitrinka skills` — the skill store.** Browse the catalog, see per-runtime
  install state, and toggle per-skill visibility for Claude Code and Codex,
  all through the native plugin mechanisms.
* **`/vitrinka:usertest`** — an autonomous exploratory user-tester skill ships
  in the plugin.

### Changed [#3-5-0-changed]

* **The plugin now installs from the public `vitrinka-kit` repo** —
  `claude plugin marketplace add FixIt-Technologies/vitrinka-kit`, spec
  `vitrinka@vitrinka`. `vitrinka install` registers the new source.
* **Machines installed under the legacy `vitrinka@lovinka` id are migrated
  automatically** by `vitrinka install` (run it once; `vitrinka doctor` shows
  a "legacy id lingers" row until you do).
* The `recall` skill dissolved into the MCP server instructions — one fewer
  skill, same pre-build memory pass.

## 3.4.2 [#3-4-2]

### Fixed [#3-4-2-fixed]

* **npm releases flow again.** The npm channel had been stuck at 3.3.0 since
  the scope move; publishing is restored, so `npm i -g @vitrinka/cli` and the
  shelf now carry the same version. No CLI behavior changes since 3.4.1.

## 3.4.1 [#3-4-1]

### Changed [#3-4-1-changed]

* **Updates come from `releases.vitrinka.ai`** — the public vitrinka release
  host. `vitrinka update` and the recorder extension no longer read the
  internal shelf domain, which was unreachable outside the company network.
  Mirrors via `$VITRINKA_RELEASE_FEED` are unchanged.

## 3.4.0 [#3-4-0]

### Changed [#3-4-0-changed]

* **The CLI targets `app.vitrinka.ai`** — the app moved off the apex in the
  host split, and the old default could only die on a redirect that drops your
  credential. Old sign-ins keep working (credentials and bindings carry over),
  and an explicit `--base`, `$VITRINKA_URL` or script still pointing at
  `vitrinka.ai` / `vitrinka.in` self-heals to the app host on its own. Nothing
  to migrate.
* **`vitrinka board create` names the board after the whole set** —
  `<project>-<branch>-<setkey>`, not the set key alone. The key defaults to
  today's date and board slugs are workspace-global, so two repos capturing on
  the same day used to ask for the very same board. Pass `--slug` to name it
  yourself; existing boards are untouched.

### Fixed [#3-4-0-fixed]

* **`--base` sticks.** Signing in (or pasting a token during `vitrinka
  install`) now persists that deployment as this machine's default, so bare
  commands — `doctor`, `watch`, `mcp` — keep talking to the install you signed
  into instead of the hosted default. No standing `$VITRINKA_URL` needed;
  logging out of it resets the default.
* **A taken slug is never adopted blindly.** When the board already belongs to
  another project, `board create` stops and tells you whose it is instead of
  importing your set onto someone else's canvas. Reuse of your own board is
  unchanged — it just says so now.

## 3.3.1 [#3-3-1]

### Fixed [#3-3-1-fixed]

* **Board listeners work again.** `vitrinka watch` no longer degrades to
  "unreachable" on quiet boards — the long-poll was being killed by the CLI's
  own 30-second network timeout, so every idle cycle counted as a failure.
  Idle holds now run their full window and quiet timeouts are treated as the
  normal heartbeat they are.
* Redirects between vitrinka hosts (e.g. `vitrinka.in` → `vitrinka.ai`) no
  longer drop your credential mid-flight (HTTPS targets only).

## 3.3.0 — unreleased [#3-3-0]

### Changed [#3-3-0-changed]

* **Package renamed to `@vitrinka/cli`** (platform packages
  `@vitrinka/cli-<os>-<arch>`). Same CLI, same `vitrinka` bin, now under the
  product's own npm scope alongside `@vitrinka/expo`. `vitrinka update` on an
  old-scope install migrates automatically (removes the legacy launcher before
  installing this one); the retired launcher is deprecated with a pointer here.

## 3.2.0 — 2026-08-23 [#3-2-0]

### Added [#3-2-0-added]

* **vitrinka now works from ANY directory.** `vitrinka install` registers the
  MCP forwarder at user scope everywhere (a bound repo also keeps its
  committed project-scope entry, which stays the preferred mechanism and
  always wins), and a new machine-level **default workspace** answers for
  directories no binding covers. Onboarding asks you to pick one; later it's
  `vitrinka config default`.
* **Path-prefix workspace rules** — map a folder subtree to a workspace
  (`vitrinka config prefix ~/Work/ClientA acme`); the longest matching rule
  wins, the default workspace is the fallback behind them.
* **A confirm gate on defaulted writes.** A write from a directory that
  resolved through a prefix rule or the default asks once per directory
  (per workspace) before landing — as a y/N in the CLI, and as a structured
  `workspace_confirm_required` re-call for MCP agents. `vitrinka config
  writes auto` turns the asking off; explicit bindings never ask.
* `vitrinka doctor` shows a `default workspace` row, and the dashboard shows
  the resolved workspace with its provenance.

### Changed [#3-2-0-changed]

* Re-running `install` no longer removes a healthy registration at a scope it
  is not rewriting — committed `.mcp.json` entries survive the global-first
  migration untouched.

## 3.1.0 — 2026-08-23 [#3-1-0]

### Added [#3-1-0-added]

* **`vitrinka mcp` — MCP registrations no longer carry your token.** The entry
  every client now gets is the same three words in every repo
  (`command: vitrinka, args: ["mcp"]`), with no URL and no credential in it.
  The command resolves the deployment, workspace, token and operator at
  request time, from the repo's committed binding and your OS keychain. That
  makes `.mcp.json` safe to commit — so a clone is born registered — and it
  gives Codex per-repo workspace routing its global, header-less registry
  could never express.

### Changed [#3-1-0-changed]

* **`vitrinka install` rewrites a legacy token-bearing registration** wherever
  it finds one — either Claude scope, or Codex's `config.toml` — and tells you
  to commit the now secret-free `.mcp.json` alongside `project.json`.
* **`vitrinka doctor` names a credential in a config file for what it is**,
  per file (`.mcp.json`, `.claude.json`, `config.toml`), with `vitrinka
  install` as the repair. It also names a registration that predates the
  forwarder instead of showing it green.

### Fixed [#3-1-0-fixed]

* A repo whose `.vitrinka/project.json` cannot be read no longer falls back to
  the default deployment — it refuses and names the file to repair, instead of
  quietly publishing to the wrong install.
* A locked or unreadable credential store is reported as itself, not as a
  generic 401 from the server, and unlocking it heals the running session
  without relaunching the client.
* The forwarder no longer outlives the client that launched it: closing the
  pipe ends in-flight work after a short grace, and a stalled handshake can no
  longer hold the process open.

## 3.0.2 — 2026-08-22 [#3-0-2]

### Added [#3-0-2-added]

* **Workspace bindings travel with the repo.** `vitrinka config bind` now
  writes a committed `.vitrinka/project.json` (per deployment, so on-prem
  coexists) — clone a bound repo on any machine and it is bound at checkout.
  The machine config remains as credentials + fallback.
* **`vitrinka install` self-heals multi-workspace setups**: writes the
  committed binding from a machine-only one, narrows an old wholesale
  `.vitrinka/` ignore so the binding can reach git, and re-registers the
  Claude MCP entry project-scope when it predates workspace baking.
* **`vitrinka doctor` explains multi-workspace problems**: binding source
  (committed vs machine-only), ignore rules hiding the binding, and stale MCP
  registrations — each row names the repair.

### Changed [#3-0-2-changed]

* Working in several workspaces on one machine no longer needs per-repo
  header surgery: the server now also resolves the workspace from the
  project a call names, and refuses (with the fix spelled out) instead of
  guessing when the answer is ambiguous.

## 3.0.1 — 2026-08-22 [#3-0-1]

### Changed [#3-0-1-changed]

* **Hosted Vitrinka now lives at `vitrinka.ai`.** New installs, MCP setup,
  desktop/Snap defaults, share links, and generated config schemas use the new
  domain. Existing credentials and workspace bindings stored for
  `vitrinka.in` continue to work during the compatibility window.

## 3.0.0 — 2026-08-18 [#3-0-0]

The CLI is now a **compiled Go binary**. The npm package is a thin
launcher: it resolves the per-platform binary package for your machine and
execs it. Same commands, same flags, same config files — no Node runtime
needed to run vitrinka, and startup is instant.

### Changed [#3-0-0-changed]

* **`vitrinka` is Go.** Source moved from `pkg/src/cli.ts` to `cmd/vitrinka` +
  `internal/cli/*` in the vitrinka repo. The npm package ships `bin/vitrinka.js`,
  which launches the per-platform binary package
  (declared as optionalDependencies, resolved by npm from whatever registry
  you already use — no postinstall download, works offline and behind proxies).
* Installing with `--omit=optional` / `--no-optional` skips the binary by
  design; the launcher says exactly which platform package is missing.

### Removed [#3-0-0-removed]

* **The `vitrinka-mcp` bin is gone.** The stdio MCP server is retired in favor
  of the server's remote endpoint. Re-register once:

  ```bash
  claude mcp remove vitrinka
  claude mcp add --scope user --transport http vitrinka https://vitrinka.ai/mcp \
    --header "Authorization: Bearer $(vitrinka token)"
  ```

  `vitrinka install` / `vitrinka doctor --fix` do this for you (Claude Code and
  Codex both).
* **The unattended fallback worker (`vitrinka-mcp work`) is gone.** `vitrinka
  watch` plus the native background monitor armed by the `listen` skill is the
  one listening lane now.
* The `mcp/server.ts`, `mcp/work.ts` and `mcp/index.ts` repo shims are deleted —
  a repo checkout no longer registers an stdio MCP; point at `/mcp` instead.

## Unreleased (pre-3.0.0 — TypeScript CLI, never published) [#unreleased-pre-3-0-0-typescript-cli-never-published]

### Removed [#unreleased-pre-3-0-0-typescript-cli-never-published-removed]

* **Claude Code UI wiring is gone.** The `install-claude` command, the
  `[Y/n]` install step and the `--claude`/`--no-claude` flags were removed —
  the statusline extension and the 🧷 board / ⧉ app footer badges never worked
  well. `install`/`update`/`uninstall` now actively strip the old wiring from
  `~/.claude/settings.json` (restoring your original `statusLine.command`).

### Added [#unreleased-pre-3-0-0-typescript-cli-never-published-added]

* **Consent-gated project indexes.** `setup-project` and `index configure` now
  create a repo-owned `vitrinka.config.json`, with extension presets, a recursive
  tri-state directory picker, strict ignore layers, exact-manifest preview, and
  per-machine approval. `index status`, `--dry-run`, and `--explain <path>` make
  the resulting policy auditable.
* **Workspaces follow the project, not the token.** `vitrinka login` now
  mints a USER-LEVEL token that acts as you in every workspace you're a
  member of; each repo binds to its own workspace (`vitrinka use`, an
  indexed table you answer by number — or automatically when exactly one of
  your workspaces has the project). Commands in an unbound repo stop with
  the same table and the fix command, so an agent asks you instead of
  writing into the wrong workspace. Workspace-pinned tokens keep working
  but are deprecated — doctor and `vitrinka update` nag to re-login.
  Per-project Claude MCP registrations bake the workspace in as an
  `X-Vitrinka-Workspace` header.
* **`vitrinka logout`** revokes this machine's token server-side
  (`DELETE /api/v1/me/tokens/current`) and removes the local file; your
  user-level tokens are listable and individually revocable via
  `GET`/`DELETE /api/v1/me/tokens[/{id}]` — a lost laptop has an answer.
* **Semantic artifacts.** `artifact-init` now scaffolds a `doc.json` you author
  plus a fixed shell rendered by the server-owned kit-3 — palette, type,
  spacing, light/dark and the design direction (editorial · dossier · terminal
  · gallery) come from the kit, and artifacts follow the viewer's theme
  automatically. `artifact-from-set` composes the doc (sections + shot
  figures) the same way. The full-HTML escape hatch lives on as
  `artifact-init --custom`, now themed by default.
* `push` warns when a hand-authored `index.html` carries HTML entities inside
  template literals (`&rarr;` renders literally under htm — write `→`).
* Findings carry their proof: `status` (verification chip), `cause`, `summary`
  (the bold conclusion) and `evidence` (mono receipt footer citing the exact
  log/file/check) joined the finding block.
* **Codex MCP registration.** `vitrinka install` now offers to register the
  remote MCP for Codex too (writes `[mcp_servers.vitrinka]` +
  `http_headers` into `$CODEX_HOME/config.toml`), `vitrinka doctor --fix`
  repairs it, and `vitrinka uninstall` removes it. Doctor/install rows are now
  `mcp·claude` / `mcp·codex`. Skills already shipped to Codex via the plugin;
  the MCP was the missing half.

### Fixed [#unreleased-pre-3-0-0-typescript-cli-never-published-fixed]

* `vitrinka tag` read option VALUES as positionals, so
  `tag add b1 auth --actor alice` sent `["auth", "alice"]` as tag names — and
  tags autocreate, so routine invocations silently minted junk tags. A flag
  placed before the verb (`tag --actor x add …`) also fell through to the `ls`
  default. Both now come from the shared `positionals()` parse the rest of the
  CLI already uses.

### Changed [#unreleased-pre-3-0-0-typescript-cli-never-published-changed]

* Project indexes are path-only v2 manifests. The CLI no longer reads or sends
  source excerpts, absolute paths, origin remotes, home-directory commands, or
  command descriptions. Missing/disabled/changed policies fail closed; tracked
  `.gitignore` matches, credential/key names, and binary/generated paths are
  denied client-side and again by the server. Deploying this release purges all
  legacy index rows once.
* **`tag merge` and `tag drop` now REFUSE without a TTY unless `--yes` is
  given** (exit 2). They previously treated "no TTY to ask on" as approval, so
  they ran unconfirmed in scripts, CI and any redirected shell — `drop`
  detaches the tag from every board, card and session. &#x2A;*If you script either
  verb, add `--yes`.**

## 1.21.2 — 2026-07-26 [#1-21-2]

### Added [#1-21-2-added]

* `vitrinka doctor` reports listener leases held by dead watch processes on this
  machine, and `vitrinka doctor --fix` releases them.
* `vitrinka extension setup | update | doctor` — install the journey-recorder
  browser extension from the CLI and keep it current. Updates now happen in
  place: the popup's "update available" banner is a button that has the CLI
  download, checksum-verify and swap the release, then reloads the extension
  into it. `--check` reports without installing; `doctor --fix` re-registers the
  native-messaging host after a CLI reinstall moves `node` or the CLI.
* The extension takes its base URL and token from this machine's CLI config on
  first load, so a fresh install needs no pasting (the options page gains a
  "Fill from the vitrinka CLI" override). Existing settings are never replaced.
* **Migrating a hand-unzipped copy:** the extension now pins its id, so the
  CLI-managed folder is a *different* extension to Chrome. Run
  `vitrinka extension setup`, load `~/.config/vitrinka/extension`, and remove
  the old card — a token you pasted by hand does not carry over.

### Changed [#1-21-2-changed]

* `vitrinka install` now detects the Chromium browsers on the machine and offers
  the recorder as a step (`--extension` / `--no-extension`), so re-running it on
  an already-onboarded machine is how you backfill the extension. An install
  that already has it silently re-registers the update host, which is also how a
  browser added since the last run starts working. `vitrinka update` keeps an
  installed extension current, and the update host is registered for the
  browsers detected on the machine (falling back to all of them when none is
  detected, so a fresh box still gets a working host) — so `extension doctor`
  can now tell you the browser you *use* is unregistered instead of counting
  four blind writes.

### Fixed [#1-21-2-fixed]

* A listener could outlive the session that armed it and hold its board's lease
  **forever**. Claude Code signals the wrapper shell it spawned, so the watch
  underneath never got the SIGTERM — it was adopted by init and kept renewing
  its lease, which the TTL cannot reap (the TTL only reaps leases that stop
  heartbeating). Annotations dispatched on that board then vanished into a
  process nobody was reading. Three fixes: `/vitrinka:listen` now arms
  `exec vitrinka watch` so the watch *is* the signalled process; `vitrinka
  watch` self-terminates if it is ever orphaned, covering exits that deliver no
  signal at all (agent SIGKILLed, crashed, terminal closed); and it now handles
  SIGHUP alongside SIGINT/SIGTERM.
* Release integrity is fail-closed: a feed without a usable SHA-256, or an
  artifact larger than it advertised, is refused before anything is downloaded
  or swapped. Previously the checksum was only verified when the field happened
  to be present, and the whole body was buffered unbounded.

## 1.21.1 — 2026-07-25 [#1-21-1]

### Fixed [#1-21-1-fixed]

* `/vitrinka:brainstorming` sometimes skipped the decision map — the map was
  reasoned out but never written, so the round opened straight into a "anything
  to cut, add, or reorder?" picker whose options referenced decisions you could
  not see anywhere, expanded output included. The map is now printed as its own
  message before any question is asked, and the board-mode exemption from
  printing it only applies once a board actually exists.

## 1.21.0 — 2026-07-25 [#1-21-0]

### Added [#1-21-0-added]

* MCP `get_session` — one recorded testing session as a compact digest (the
  screen walk, issues deduped across steps with live board status, per-route
  web vitals, and facets to group them into fix batches). Address it by session
  id or by the session's board slug. Use it instead of walking raw `/events`.

### Changed [#1-21-0-changed]

* Closing a session no longer waits for its board. The server builds the board
  on a worker and reports `projection` state, so a stop returns immediately —
  and a recording session's board is now kept live as you test, rather than
  only existing once the session ends. Anything reading `projectionError` off a
  stop response should read `projection.state` instead.

## 1.20.0 — 2026-07-23 [#1-20-0]

### Changed [#1-20-0-changed]

* All output now lives under one `.vitrinka/` home in the target repo:
  `.vitrinka/screenshots/` (was `.screenshots/`), `.vitrinka/artifacts/<slug>/`
  (was `.artifacts/`), plus `.vitrinka/scratch/<topic>/` as the sanctioned spot
  for ad-hoc session output. Legacy roots auto-migrate the first time a command
  touches them; explicit `--root`/`--from` paths are respected as given.

### Added [#1-20-0-added]

* `vitrinka tidy [--yes] [--all]` — report-first sweep of legacy roots and
  shot-shaped repo-root litter (`.screenshots-<topic>/`, loose `shot-*.png`)
  into `.vitrinka/`; `--all` also moves loose images and `.aud_*` text litter.
  Git-tracked files are never touched.
* Plugin skills `/vitrinka:annotations` + `/vitrinka:answers` — manual MCP
  fallback drains for when a "Send to Claude" dispatch doesn't reach the
  session (annotation work queue / durable question answers).

## 1.19.0 — 2026-07-23 [#1-19-0]

### Added [#1-19-0-added]

* `vitrinka setup-project` — the repo onboarding wizard (clack-styled TUI, the
  pkg's first runtime deps: `@clack/prompts` + `picocolors`). One front door for
  everything a repo needs before the editor and board @ autocomplete work:
  machine-install gate (offers `install` inline when the machine isn't
  onboarded), project name + registry record, file/route index push (verified),
  component-index with auto-detected candidate dirs, desktop-app check, and an
  optional hello board. Status-first and idempotent — re-runs refresh the cheap
  bits and only prompt for what's missing; `--yes`/non-TTY takes every default.
* `edit` and `status` print a one-line hint toward `vitrinka setup-project`
  while the repo has never been indexed.

## 1.18.0 — 2026-07-21 [#1-18-0]

### Added [#1-18-0-added]

* `vitrinka edit [path]` — focus the resident Vitrinka.app editor on a file or
  project via the `vitrinka://edit?path=…` deep link (`--print` emits only the
  URL; when the app isn't installed it prints an install hint). The instant
  editor's CLI entry point.
* Automatic project registry: `edit`, `status`, `watch`, and `board-from-set`
  now record `{project → main-worktree path}` to
  `~/.config/vitrinka/projects.json` as a side effect, so the desktop editor
  auto-discovers your live checkouts with zero extra UX. Best-effort — a write
  failure never breaks the command.
* `compose_board` accepts kind `"artifact"` — a sandboxed self-contained HTML
  card straight from a compose batch: `{html}` full document or
  `{body, runtime: "tw4"}` markup-only (server inlines the Tailwind v4
  runtime; dramatically cheaper). Up to 8 MiB inline.

### Changed [#1-18-0-changed]

* `compose_board`'s card schema is slim: the tool description now carries the
  kind index + quick shapes only; per-kind payload contracts live in the
  vitrinka plugin skills (the publish skill's `references/card-kinds.md` is
  the index). Unknown kinds still 400 with the full allowed list.
* `vitrinka import` (and `POST /import`) replies with a compact per-card
  summary (`{id, elemNo, kind, title, counts, section?}`); pass `--verbose`
  (API: `?verbose=1`) for the full card JSON.

## 1.17.0 — 2026-07-20 [#1-17-0]

### Added [#1-17-0-added]

* `get_questions` MCP tool — durable post-send retrieval of a board's answered
  decisions (options, notes, ⌖ refs). Choices ride the work wire exactly once,
  so a send with no listening session used to look unrecoverable; now any
  session can re-read the full answer record at any time.
* `scrape_board` digest now carries a compact `questions[]`
  (`{id, prompt, answer?, note?, staged?}`) — one call reads the decision
  state alongside cards/flow/sections; section scopes keep only their own.
* `vitrinka watch` keepalive: while a human actually has the board open, the
  leased watch emits a quiet `· keepalive` heartbeat (default every 3000 s,
  `--keepalive 0` off) so the listening session's prompt cache stays warm and
  the next annotation lands cheap. An abandoned board costs nothing.

## 1.16.0 — 2026-07-19 [#1-16-0]

### Added [#1-16-0-added]

* Listener takeover is automatic on the same machine — arming `vitrinka watch`
  for a scope another of YOUR sessions already holds displaces the older
  listener (its in-flight items go back to the queue) instead of failing with
  a 409 and a manual handoff. The displaced watch prints one stand-down line
  and exits cleanly. Live leases on a *different* machine still refuse the
  claim; `--takeover` still steals expired leases only.

### Fixed [#1-16-0-fixed]

* `vitrinka watch` and the background daemon no longer swallow answered
  brainstorm choices — a pure choice batch used to be drained by the notifier
  and marked delivered while the listening session never woke. Notifier lanes
  now peek without consuming, and watch announces each answer as a
  `№<id> [choice]` line; the acting lane keeps exactly-once delivery.

## 1.15.1 — 2026-07-18 [#1-15-1]

### Added [#1-15-1-added]

* `vitrinka version` (and `--version`/`-V` for the bare number) — the installed
  version, where it runs from (repo checkout / bun / pnpm / npm install), and
  the last known update state from the notifier cache. No network, instant.

### Fixed [#1-15-1-fixed]

* `vitrinka update` now updates through the package manager that OWNS the
  running copy (bun / pnpm / npm, detected from the executable's path) and
  verifies the running file actually changed — a bun-installed CLI used to
  `npm i -g` into a shadowed prefix and stay stale forever, and update warns
  loudly when another install still shadows it on PATH.

## 1.15.0 — 2026-07-18 [#1-15-0]

### Added [#1-15-0-added]

* Update notifier v2 — the daily detached check now asks YOUR vitrinka server
  first (new `GET /api/v1/version`: a deploy announces the version, works on
  the mesh with npm unreachable) and falls back to the npm registry. Agent
  shells (non-TTY — an AI session driving the CLI) see the
  `update available … · run: vitrinka update` line on every run so the session
  can offer the update; interactive terminals see it at most once a day.

## 1.14.0 — 2026-07-18 [#1-14-0]

### Added [#1-14-0-added]

* `compose_board&#x60; question options accept &#x2A;*`cardRef`** — trace an option to a
  card created in the SAME compose batch (template + inline cards share one ref
  space), so a decision map and its per-option architecture diagrams land in
  ONE call. `cardId` still traces to existing cards; picking flies only on
  ⌘-click now (plain click just stages the answer).
* The built-in &#x2A;*`decision-map`** template is documented on `compose_board`:
  structured params `{title, context, decisions:[{key, title, stakes, options,
  multiSelect?, kind?: "text"}]}` render the whole brainstorm map — a
  width-filling grid of merged decision tiles — deterministically server-side.

## 1.13.0 — 2026-07-17 [#1-13-0]

### Added [#1-13-0-added]

* `vitrinka import <file>` + `vitrinka schema push` — refreshable ground-truth
  diagrams from docker-compose / OpenAPI / SQL DDL / a live Postgres;
  `--section` lands them inside a journey frame, `--rev` stamps the git SHA.
* Journey branch capture: `snap --next/--target` groups + journey-from-set.
* Unified **publish** skill (session · journey · docs · board · artifact).
* `compose_board` edges accept `fromRegion`/`toRegion`; `arrange` gains a
  journey mode.

### Fixed [#1-13-0-fixed]

* `add --file` hardened (out-of-root copies, symlinks, non-regular files);
  `push` guards against a huge `--root` and reports tar failures legibly.
* `doctor` no longer health-checks every Claude MCP server.

## 1.11.0 — 2026-07-15 [#1-11-0]

### Added [#1-11-0-added]

* `vitrinka upload <files…> --board <slug>` — put PDFs, documents, and images
  on a board as cards straight from disk (the bytes never pass through an AI
  context). PDFs land as live embedded document cards, other files as download
  chips. Placement is intent-based: `--section "Name"` lands inside a journey
  frame (grows to fit), `--anchor <cardId> [--side right|below]` docks beside a
  card, and no flag = server free placement (never a 0,0 pile).

## 1.10.1 — 2026-07-13 [#1-10-1]

### Changed [#1-10-1-changed]

* Canonical domain is now **vitrinka.in** — the CLI/MCP default base URL,
  skills and docs all point there. vitrinka.lovinka.com keeps working as an
  alias; override per-machine with `VITRINKA_URL` as before. Re-run
  `vitrinka install` (or `doctor --fix`) to re-register MCP on the new host.

## 1.10.0 — 2026-07-13 [#1-10-0]

### Added [#1-10-0-added]

* `compose_board` question options accept `cardId` — the board draws a trace
  wire from the option to the card it names, hovering glows it, and picking
  flies the camera there. Pair it with the new `POST /artifact` params
  (`section`, `device`, `anchor`/`side`): mockups land inside their take's
  section frame, sized by device intent, and scale to fit their card
  automatically (server-measured natural size).

## 1.9.0 — 2026-07-12 [#1-9-0]

### Changed [#1-9-0-changed]

* MCP registration moved to the remote `/mcp` endpoint (streamable HTTP) —
  no local stdio process per session; `vitrinka install`/`doctor --fix`
  re-register automatically. The old stdio bin is deprecated.

## 1.8.3 — 2026-07-11 [#1-8-3]

### Fixed [#1-8-3-fixed]

* Interactive prompts (`install`'s operator question, any `promptLine`) strip
  ANSI escape sequences and control characters — arrow-key edits no longer
  persist raw `ESC[D`/`ESC[C` bytes into the operator name (which every fresh
  browser adopted as its board identity). `vitrinka operator <name>` sanitizes
  the same way, and the server now rejects control characters with 422.

## 1.8.2 — 2026-07-11 [#1-8-2]

### Added [#1-8-2-added]

* Desktop-app flag: `~/.config/vitrinka/desktop-app` is the one canonical
  "is Vitrinka.app installed" answer (content = app path). Stamped by
  `apps/desktop make install`, re-synced by `install-claude` and `doctor`.
* `install-claude&#x60; adds a second **⧉ app** footer badge (only when the app is
  installed) whose `vitrinka://` deep link opens the board in Vitrinka.app.
* `vitrinka open` opens the board in Vitrinka.app when installed;
  `--browser` forces the browser.

## 1.8.1 — 2026-07-11 [#1-8-1]

### Added [#1-8-1-added]

* `install-claude` now also wires a `SessionStart` hook
  (`~/.claude/hooks/vitrinka-board-link.sh`) that puts the repo's live board
  URL into session context at start — the clickable 🧷 footer badge exists
  from message one instead of waiting for a board URL to be mentioned.
  `vitrinka doctor` reports (and `--fix` repairs) the hook; `uninstall`
  removes it.

## 1.8.0 — 2026-07-11 [#1-8-0]

### Changed [#1-8-0-changed]

* Skills now distribute EXCLUSIVELY as the vitrinka plugin — for Claude Code
  (`claude plugin install vitrinka@lovinka`) AND Codex (`codex plugin add
  vitrinka`), both from the `LEFTEQ/vitrinka` marketplace. `vitrinka install`,
  `doctor --fix` and `update` install/refresh the plugin in every runtime CLI
  they find; the npm package no longer bundles the skills.
* Skills layout flattened: `listen` is a first-class skill
  (`/vitrinka:listen` in Claude Code, `$listen` in Codex).

### Removed [#1-8-0-removed]

* `vitrinka install-skills` — superseded by the plugin. `vitrinka uninstall`
  still clears legacy `~/.claude/skills` copies from pre-plugin machines.

## 1.6.0 — 2026-07-08 [#1-6-0]

### Added [#1-6-0-added]

* `vitrinka update` — updates the CLI (git pull in a checkout, `npm i -g` when
  behind the registry), prints what's new, and refreshes the installed skills
  and Claude UI wiring.
* Status-first onboarding: `vitrinka install` now prints a ✓/✗ setup table and
  handles everything install.sh used to (MCP registration, write token,
  operator persona, env checks) — asking only for what's missing.
* `vitrinka uninstall [--purge]` — clean offboarding (skills, shim, completion,
  Claude wiring, MCP registration; `--purge` also removes token + operator).
* `vitrinka doctor --fix` — repairs stale skills, a stale/missing shim, a lost
  MCP registration, and the Claude UI wiring.
* `vitrinka open [--qr]` — opens the capture root's live board; `--qr` renders
  a terminal QR code to scan with the iPad.
* `vitrinka status` — session dashboard: live board, open work queue, and the
  listener lease for the current repo+branch.
* Passive update notifier (one dim line, cached daily, npm installs only) and a
  first-run hint on fully unconfigured machines.

### Removed [#1-6-0-removed]

* `install.sh` — the CLI is the installer now: `vitrinka install` (or
  `node pkg/src/cli.ts install` from a checkout).

## 1.5.0 — 2026-07-08 [#1-5-0]

* project-memory MCP tools + link/board compose vocabulary (pre-changelog release).
