Vocion 2.123
A client document cannot be exported until it has been read as the buyer. One Marketplace with two tabs is where you add capability; Teams is where you see what you already have. The Review queue calls a proposed action what it is — a recommendation. And the data-room collector files things, which it had never once done in production.

What you can do now that you could not yesterday
- Send a client document knowing something sceptical has read it. A proposal cannot be exported until it has been read as the buyer at its current version, and a blocking finding refuses the export with the sheet, the rule and the edit that answers it.
- Find everything this workspace can turn on in one place — plugins and unhired agents, two tabs, one page — instead of hunting between a plugins page and a team page.
- Read your queue as a list of recommendations, each one saying which agent recommends it, rather than a list of "proposals" that read like documents.
- Open a data room and find the material already in it. The collector files what clearly belongs to a room after every sync, with its confidence and a one-click undo. Until this release it had never filed anything in production.
- Watch a long document run make progress — "sheet 7 of 12" on the running step line, rather than a spinner for four minutes.
- See what a plugin has learned from being used, adopted rules included, on the same panel that shows what it is for.
- Tell at a glance which numbers in a document are not yet real, because an unbaselined
figure now renders as an amber
to be baselinedchip instead of quietly reading as a fact.
The export gate
export_document_pdf will not produce a file for a client-facing document whose current version
has not been read as the buyer. The gated set defaults to the proposal, scope and
partnership-update playbooks and is overridable per workspace via defaults.clientFacingPlaybooks
in workspace.yaml. Nothing about it is a silent block: if the document has never been reviewed,
the export runs red_team_document itself first and then decides.
A blocking finding refuses the export and says exactly what would have to change — NOT exported,
the model that read it, the version, then each blocking finding as
sheet <n> · <rule>: <finding> → <fix>, and the instruction to fix by sheet and re-run. A person
can still override and say which finding they are overriding and why; there is no approval queue
in the middle. Either way the document carries a chip — not read as the buyer,
read as the buyer · 2 blocking, read as the buyer · 3 to fix, read as the buyer · clean —
beside the render-verify chip, so the state is visible before anyone asks for a file.
See Proposals, and The critic that ran out of room for why the reviewer underneath it needed rebuilding first. (#461)
Marketplace, with tabs — and what that leaves on Teams
/dashboard/marketplace is now one page with two tabs: Plugins and Agents for hire. The
old /dashboard/plugins route 308-redirects to it. Turning a plugin on still does exactly what
it did — writes one line into plugins: in workspace.yaml, preserving comments, and applies
it, with the new workspace sha as the receipt.
The split is the point. Teams & agents is where you go to see the workforce you have. Marketplace is where you go to add capability. Nothing acquisitive lives on Teams any more, and the "see also" cross-link rows that used to push people back and forth between the two are gone from Teams & agents, Skills & tools and Evals.
Two tabs, one page. The Agents for hire tab lists the catalogue agents this workspace has not hired yet.
Recommendations in Review
An action an agent wants to take — a CRM update, an email, an enrollment — is labelled a
Recommendation in the queue, and its subline reads recommended by <agent>. It used to be
called a proposal, which collided with the noun for the document the Proposals app writes, in a
product that has both.
The word had to come from somewhere, so the kind that was previously called a recommendation — an agent offering you options to pick between — is now Choice. Worth knowing if you have been reading the queue for a while: Recommendation is the one that executes when you approve it, Choice is the one that asks you to pick.
This is surface text only. The stored kind column, proposeAction, the action_run rows and
every ?kind=proposal URL are untouched, so nothing you have bookmarked or scripted moves.
Three open decisions, oldest first. The kind is the second line of each row, not a filter you have to remember.
(#464)
The collector files things now
After every source sync, the data-room collector scores recent material against open rooms, files what clearly belongs, and asks about what is merely plausible. It shipped earlier this week and had never filed a single thing in production: its candidate query was pinned to the sixty seconds around the sync that had just run, so it only ever saw what that connector had just changed.
It now looks across the room's real window — COLLECT_WINDOW_DAYS, thirty days — for anything
naming an open room by domain, client, codename or alias. On the deployment we operate, the
first sync after the fix filed seven sources onto one live room at 80 to 100 percent
confidence, and the sync after that filed nothing new, which is the other half of the proof.
Full write-up in A query that only ever saw the last hour; the feature itself is Data rooms. (#463)
Per-sheet progress on the running step line
A twelve-sheet document render is minutes of work behind one step label. The document tools now
emit a step_progress event carrying a short note, and the running step line reads
"Rendering the document… sheet 7 of 12".
No new surface: the note attaches to the trace node already on screen, on the same timeline that shows every other step. The red team is deliberately exempt — it is one model call over the whole document, so the only honest note is how many sheets it is reading, and inventing a per-sheet count there would be a progress bar that means nothing. (#462)
What a plugin learned from being used
The plugin outcome panel — on Data rooms, on Proposals, and on any plugin page — gained a Learned from use group. It was reading only the pending learning-candidate queue, which meant a rule that had already been adopted vanished from the panel at the exact moment it started working. It now reads adopted rules from the plugin's agents' memory namespaces, merges them with still-pending candidates, drops a candidate whose rule is already adopted, and shows the newest five.
Empty state: "Nothing learned yet — corrections in chat and Review decisions land here." See Learnings. (#459)
The placeholder chip
The proposal framework has always required that an unbaselined number be shown as a placeholder in the open rather than as a confident figure — it is one of the red team's ten rules, and an unsourced figure that is not marked is a blocking finding. The chip that marks it was specified in the skill and never defined in the stylesheet, so it rendered as ordinary text and the distinction was invisible on the page.
The chip is the difference between "we have not measured this yet" and a number the buyer will hold you to.
(#458)
Real tooltips
Four controls that explained themselves with a native title= attribute now use the Tooltip
component: the build-version link in the sidebar, the source-token show/hide toggle, the
model-strength gauge under an agent message, and the Remove control on a room's source. Native
title has a delay you cannot set, styling you cannot touch, and no existence at all on touch.
The aria-labels stayed where they were, so nothing changed for a screen reader.
(#450)
For operators: the deploy prints a receipt
Measured on the box: the app container was starting 40 seconds before the database dump finished and 59 seconds before the last migration applied — so every deploy served new code against the old schema for about a minute — and the container swap closed every open server-sent-events stream, which a person mid-answer reads as a network error.
Migrations are now their own script that runs before the swap and refuses to swap on a failed or
empty dump, a real migration error, or an object a migration declared and did not create. The
swap drains in-flight requests first, bounded and never fatal, and the new container has to
serve its own pinned sha before the deploy continues. Every phase prints a [deploy-receipt]
line.
The first run on the new flow also turned up eight files in migrations/concurrent/, declaring
nine indexes, that had never been applied in production — which is precisely the failure the
repo's own migration conventions warn a hand-rolled applier about.
Full write-up: Two deploys in one, and the forty-second gap.
The scripts live in a private deployment repository and are described there as practices rather
than cited as paths; the public half is packages/core/migrations/CONVENTIONS.md and the
migrations/concurrent/ directory in vocion-core.
Fixed
- The red-team reviewer returned nothing parseable on long documents. Its answer is now a declared tool call with an 8000-token budget, and a partial answer keeps the findings that validate instead of dropping all of them. (#457)
- The data-room collector never filed anything, because its candidate query could not reach past the last sync. (#463)
emailsOfreturned the rawFromheader, so a counterparty domain arrived with a trailing>and matched no room, ever. (#463)- The plugin panel hid a learning the moment it was adopted, showing only the pending queue. (#459)
- The
.phplaceholder chip was specified but never styled, so unbaselined numbers rendered as plain text. (#458) - Migrations ran after the container swap, and the concurrent index directory was never applied at all.
Known
- Repeated red-team passes surface different findings. The rubric is ten rules and the reviewer is capped at fifteen findings per pass, so two runs over the same unchanged document return two overlapping samples rather than the same set. The receipt's own instruction — fix, re-run, ship when it comes back clean — therefore has no fixed point. Blocking only on findings that recur across passes is the design question, and it is not built.
- The red team is one model call with no retry. A parse failure returns
skippedwith a reason and stops. - The collector's silence is still indistinguishable from correctness. Nothing yet notices a collector that has run a hundred times and filed nothing, which is how the bug above survived.
- The
drain=timeoutandswap=pin-mismatchdeploy receipts have never fired on a real deploy. They are code that has run in tests, not behaviour observed in anger. - Recommendation and Choice are labels. The stored kinds, the API parameters and the URLs keep the old names, so an API response and the screen can disagree on the word.
- Everything above is live: the deployment serves
v2.123.1built from4eee5c35, the same sha the deploy receipt reports.
Turning things on
Two doors to the same edit, and the workspace file stays the source of truth either way:
# workspace.yaml
plugins: [wiki, data-rooms, proposals]
Or open Marketplace (/dashboard/marketplace) and use the switch on the Plugins tab — it
writes that same line, preserving your comments, applies it, and hands you the new workspace sha
as the receipt. Dependencies come along: plugins: [proposals] loads data-rooms first.
Turning a plugin off removes its pages, nav rows, agents, automations and team from the applied
workspace; nothing it wrote — artifacts, rooms, learnings — is deleted.
The Agents for hire tab on the same page is where an agent from the catalogue joins the workspace. Once it has, it appears on Teams & agents with the rest of the workforce, and not here.
Where this lives in the repo
- Core:
vocion/vocion-core,mainat4eee5c35 - Pull requests in this release: #450, #457, #458, #459, #461, #462, #463, #464
- Migration in this release:
0125_project_client_facing_playbooks - The feeds are feature releases and developer updates, summarised on /releases and available as RSS.