From 1c5e0d169989e9acae4c1c2708f10fc638b447c8 Mon Sep 17 00:00:00 2001 From: olegshmuelov Date: Mon, 10 Aug 2026 15:02:17 +0300 Subject: [PATCH 1/4] feat(brains): sync the generated capability catalog and bump to 2.9.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Regenerate the published capability artifacts from the current catalog in ssvlabs/brains. brains-build/SKILL.md gains the three feature rows an earlier sync missed — Board form, Sharing, Recipe publishing — with the routing wording as it now stands; the other four generated skills render byte-identical and do not move. capability-catalog.json carries the refreshed digests and the commit that rendered these bytes. Both client manifests advance 2.8.3 -> 2.9.0 together, so the new content reaches installed plugins and the two stay version-aligned. --- plugins/brains/.claude-plugin/plugin.json | 2 +- plugins/brains/.codex-plugin/plugin.json | 2 +- plugins/brains/generated/capability-catalog.json | 6 +++--- plugins/brains/skills/brains-build/SKILL.md | 3 +++ 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/plugins/brains/.claude-plugin/plugin.json b/plugins/brains/.claude-plugin/plugin.json index 5482872..70b13ff 100644 --- a/plugins/brains/.claude-plugin/plugin.json +++ b/plugins/brains/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "brains", "description": "Your memory layer: Gmail, Calendar, Drive, and prior Claude conversations as queryable pages, with reflexive recall, hook-driven turn-by-turn capture and inbox delivery, and boards/automations/workflows on top.", - "version": "2.8.3", + "version": "2.9.0", "author": { "name": "brains (ssvlabs)" }, diff --git a/plugins/brains/.codex-plugin/plugin.json b/plugins/brains/.codex-plugin/plugin.json index f715ee6..52ab452 100644 --- a/plugins/brains/.codex-plugin/plugin.json +++ b/plugins/brains/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "brains", - "version": "2.8.3", + "version": "2.9.0", "description": "Your personal memory layer for Codex: query Gmail, Calendar, Drive, and prior conversations, then build boards, automations, and workflows.", "author": { "name": "brains (ssvlabs)", diff --git a/plugins/brains/generated/capability-catalog.json b/plugins/brains/generated/capability-catalog.json index 4171ad4..4a44569 100644 --- a/plugins/brains/generated/capability-catalog.json +++ b/plugins/brains/generated/capability-catalog.json @@ -13,9 +13,9 @@ }, { "capability_id": "brains-features", - "catalog_sha256": "6086cbe9877b2005b3cea06611ba74ab7b527eaed04af39e5a815c94c8806b37", + "catalog_sha256": "a1677c30594d44e516a3aaa8e6524829360e44d90f8304d2ab8291b7e5ed6a3a", "artifact_path": "plugins/brains/skills/brains-build/SKILL.md", - "artifact_sha256": "753afa4d8b4833b91622fa8c55d2bc76304e9a9170faed3365c7febd53836ae0" + "artifact_sha256": "f9196254dd49af3cd8bb1d4d93b1ba36ff10d8efc0bb039fb464901e143e143f" }, { "capability_id": "procedure:board", @@ -36,5 +36,5 @@ "artifact_sha256": "b22814fd630ed78964d54440ec327e3aebcde8c812aaa9985fff9c4d345d3f09" } ], - "source_commit": "864c45fa7e1c4b4d60116b6697abc596a272fbff" + "source_commit": "9f354fc395e7434968aa37bc3bf68c3ec72eb297" } diff --git a/plugins/brains/skills/brains-build/SKILL.md b/plugins/brains/skills/brains-build/SKILL.md index 65b4feb..2ca0059 100644 --- a/plugins/brains/skills/brains-build/SKILL.md +++ b/plugins/brains/skills/brains-build/SKILL.md @@ -15,6 +15,7 @@ Don't restate that artifact here — open it. |---|---|---| | **Board** | A spreadsheet-like dataset on a brain — rows are JSON, shared and queryable. | `create_board_flow` if your client exposes it (follow the playbook it returns), otherwise `create_board` | | **Board skill** | A named, saved LLM action over a board's rows. | `create_board_skill` | +| **Board form** | A shareable page where someone else answers your questions in chat — one board row per submission. | `create_board_form` | | **Bulk upload** | Load a large or arbitrary-format file into a board without reading it into context. | `create_import_grant` → `bulk_append_rows` | | **Dashboard** | A live, interactive view of a board that you author as a full HTML app. | `get_dashboard` → `set_dashboard` | | **Page** | A first-class note in a brain. | `create_page` | @@ -22,9 +23,11 @@ Don't restate that artifact here — open it. | **Recurring brief / digest** | Install a prebuilt automation that sends a recurring summary instead of scaffolding one. | `list_recipes` → `install_recipe` | | **Workflow** | A goal container: charter, KPIs, deadlines, roster, an owned board and template automations. | `create_workflow_flow` if your client exposes it (follow the playbook it returns), otherwise `create_workflow` | | **Mini-site** | Static sandboxed HTML on a brain — a deck, a one-pager, a shareable link. | `create_mini_site` | +| **Sharing** | Give someone access — share_board, share_folder (cascades), share_mini_site, create_share_circle. | `share_board` | | **Board source link** | Bind a board to an external resource through an adapter (http_json, ics, github, monday). | `create_board_link` | | **Dataset recipe** | Schedule a board link to materialize into a dataset on a cron — set up on the board's sources page. | the board's Sources page (`/boards//sources`) — no agent tool for this | | **Integrations** | See what's connected and install, upgrade or remove an integration. | the `brains-integrations` skill if your client loads skills, otherwise `list_my_integrations` → `install_integration` | +| **Recipe publishing** | Publish a board or other supported build to the catalog — publish_recipe names the kinds it accepts. | `publish_recipe` | | **Telegram bot** | Reach the brain from a phone, without the laptop. | the Connect Telegram page (`/install/telegram`) — no agent tool for this | Reach for a `*_flow` tool for any create/set-up/scaffold ask: it returns the From a804505ed11e9afaf28fcc28cb5364bbce175c61 Mon Sep 17 00:00:00 2001 From: olegshmuelov Date: Tue, 11 Aug 2026 14:20:26 +0300 Subject: [PATCH 2/4] feat(brains): advance the catalog pin to the release production serves MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-renders the published artifacts from the current source commit, so source_commit names the release a user's MCP server is actually running rather than the earlier one this branch first pinned. brains-write/SKILL.md picks up the integration-action contract change: a user who enables direct execution in their brains settings has a short allowlist of low-risk actions run inline from a local CLI client, so an agent must treat any act_on_integration call as potentially executing and never assume a draft_id. Executions are still recorded in /inbox. brains-automation/SKILL.md states the automation-side rule instead — a confirmation-required action drafts unless the automation's owner set write_policy:'auto_confirm_safe'. brains-build/SKILL.md is unchanged from the previous commit; its rows are already at the rendered text for this commit. The manifest advances two artifact digests and source_commit. Both client manifests already carry 2.9.0 from the previous commit, which is the single release that delivers all of this. --- plugins/brains/generated/capability-catalog.json | 6 +++--- plugins/brains/skills/brains-automation/SKILL.md | 5 +++-- plugins/brains/skills/brains-write/SKILL.md | 9 +++++++-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/plugins/brains/generated/capability-catalog.json b/plugins/brains/generated/capability-catalog.json index 4a44569..5d698d7 100644 --- a/plugins/brains/generated/capability-catalog.json +++ b/plugins/brains/generated/capability-catalog.json @@ -9,7 +9,7 @@ "capability_id": "integration-actions", "catalog_sha256": "29f58ddc5dab294814934fe7af0d808f4b7dc441e3c3d29bb0e67fa705d7add4", "artifact_path": "plugins/brains/skills/brains-write/SKILL.md", - "artifact_sha256": "b27acd2ffc1ba4914890a88dba00e71f7052bd738369f28f9d86a1566bd2f548" + "artifact_sha256": "21f7cd40bedb7602efbbd43f718c1e5e78e41a19de3d96f3dd599b95fb17732d" }, { "capability_id": "brains-features", @@ -27,7 +27,7 @@ "capability_id": "procedure:automation", "catalog_sha256": "ea1e883788e138e39b80627e5a14d6ac35dc7f658b4628e7e32084bb6620fe8b", "artifact_path": "plugins/brains/skills/brains-automation/SKILL.md", - "artifact_sha256": "e71b5648d93e611de49fd81549217db8232427262e282af6d1c1d70c9c2e17c9" + "artifact_sha256": "78c63d6a09ffdf55f80f3b686b1cd0d1000585bc1d5d148323ea03ebda018bc2" }, { "capability_id": "procedure:workflow", @@ -36,5 +36,5 @@ "artifact_sha256": "b22814fd630ed78964d54440ec327e3aebcde8c812aaa9985fff9c4d345d3f09" } ], - "source_commit": "9f354fc395e7434968aa37bc3bf68c3ec72eb297" + "source_commit": "e332512644b2f06a6b7068dcc9196eab2d31c05a" } diff --git a/plugins/brains/skills/brains-automation/SKILL.md b/plugins/brains/skills/brains-automation/SKILL.md index c536dbf..4cb3388 100644 --- a/plugins/brains/skills/brains-automation/SKILL.md +++ b/plugins/brains/skills/brains-automation/SKILL.md @@ -149,8 +149,9 @@ It returns slug/title/snippet, so call `get_page` on the selected slug to read frontmatter. Use its `install_id`, `action_name`, and structured `input` in `brains.act`; this tuple is the only call shape. Partial tuples error; only bare legacy `source` returns `clarification`. -`requires_confirmation:true` drafts for out-of-band approval; -`requires_confirmation:false` runs inline. If +`requires_confirmation:true` drafts for out-of-band approval unless this +automation's owner set `write_policy:'auto_confirm_safe'`, which executes it +inline; `requires_confirmation:false` runs inline either way. If `requires_confirmation` is absent, the page predates the field: treat whether it drafts or runs as unknown. `side_effect` says where it writes (`external` = the provider, visible outside brains; diff --git a/plugins/brains/skills/brains-write/SKILL.md b/plugins/brains/skills/brains-write/SKILL.md index 4be3069..e98d9e7 100644 --- a/plugins/brains/skills/brains-write/SKILL.md +++ b/plugins/brains/skills/brains-write/SKILL.md @@ -17,8 +17,13 @@ It returns slug/title/snippet, so call `get_page` on the selected slug to read frontmatter. Use its `install_id`, `action_name`, and structured `input` in `act_on_integration`; this tuple is the only call shape. Partial tuples error; only bare legacy `source` returns `clarification`. -`requires_confirmation:true` drafts for out-of-band approval; -`requires_confirmation:false` runs inline. If +`requires_confirmation:true` normally drafts for out-of-band approval; +`requires_confirmation:false` runs inline. A user may additionally enable direct +execution in their brains settings, in which case a short allowlist of low-risk +actions also runs inline when called from an eligible client signed in from the +user's own computer (CLI clients such as Claude Code and Codex) — every execution +is still recorded in /inbox. The mode is decided server-side per call, so treat +any call as potentially executing and never assume a `draft_id`. If `requires_confirmation` is absent, the page predates the field: treat whether it drafts or runs as unknown. `side_effect` says where it writes (`external` = the provider, visible outside brains; From 413dd86b250c8f1ccfa8ee7a6b43ea119860110f Mon Sep 17 00:00:00 2001 From: olegshmuelov Date: Tue, 11 Aug 2026 14:30:25 +0300 Subject: [PATCH 3/4] test(plugin-contract): pin the action contract's per-call execution rule MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existing branch assertion pinned "`requires_confirmation:true` drafts for out-of-band approval" as an absolute. That is no longer true server-side: a user who enables direct execution has an allowlisted low-risk action run inline from a local CLI client despite requires_confirmation:true. The rendered contract now says "normally drafts", so the pin follows the truth. Its other two clauses are untouched. Adds a pin for the rule that carries the weight once true can execute: the mode is decided server-side per call, so the caller must never assume a draft_id. It is one contiguous substring of the rendered sentence rather than separate includes for each phrase, because two fragments can sit in different sentences or under a contradicting condition and still both be found — that pins vocabulary, not the rule. Mutation-proved both. Reverting to the old absolute wording, deleting the auto_executed row, rewording the false branch, and deleting, paraphrasing, splitting or inverting the per-call rule each turn the suite red with the right message. The artifact digest gate fires first on a lone edit, so each mutation also resealed the manifest — the coordinated edit that clears provenance and is exactly what these semantic pins are for. --- tests/plugin-contract/run.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/tests/plugin-contract/run.ts b/tests/plugin-contract/run.ts index 1670e62..e70fb50 100644 --- a/tests/plugin-contract/run.ts +++ b/tests/plugin-contract/run.ts @@ -1615,12 +1615,29 @@ assert( writeSkillNormalized.includes("treat whether it drafts or runs as unknown"), "absent requires_confirmation must remain unknown rather than predict a draft", ); +// `normally` is load-bearing, not hedging: a user who enables direct execution +// has an allowlisted action run inline from a local CLI despite +// `requires_confirmation:true`. The absolute wording this used to pin became +// false server-side, so the pin follows the truth rather than the other way +// round. The other two clauses are unchanged. assert( - writeSkillNormalized.includes("`requires_confirmation:true` drafts for out-of-band approval") && + writeSkillNormalized.includes("`requires_confirmation:true` normally drafts for out-of-band approval") && writeSkillNormalized.includes("`requires_confirmation:false` runs inline") && writeSkillNormalized.includes("| `auto_executed` | It already ran; it carries `result` and `action_record_id`."), "both requires_confirmation branches must retain their distinct behavior", ); +// The rule that carries the weight once `true` can execute: the caller must not +// plan on a draft. Pinned as ONE CONTIGUOUS run of the rendered sentence, not as +// separate `includes` calls for "decided server-side" and "never assume a +// `draft_id`" — two fragments can each be present in different sentences, in +// different sections, or under a contradicting condition, which pins the +// vocabulary while letting the rule itself be reworded away. +assert( + writeSkillNormalized.includes( + "The mode is decided server-side per call, so treat any call as potentially executing and never assume a `draft_id`.", + ), + "per-call server-side mode and the never-assume-a-draft_id rule must stay one intact sentence", +); assert( writeSkillNormalized.includes("Partial tuples error") && writeSkillNormalized.includes("only bare legacy `source` returns `clarification`"), From a48cead977cccb2e17ee37eae9c60fe6c2be3312 Mon Sep 17 00:00:00 2001 From: olegshmuelov Date: Tue, 11 Aug 2026 15:02:15 +0300 Subject: [PATCH 4/4] test(plugin-contract): pin the whole action contract, not sentences in it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Presence checks cannot see what sits next to the sentence they find. Review demonstrated it: leave every pinned sentence intact, insert "the setting is off for almost everyone, so plan on the draft" straight after the never-assume-a- draft_id rule, refresh the digests, and the suite passed while the contract now told an agent the opposite of what it means. Pins the whole rendered contract paragraph as one verbatim region, following the README_INTRO_REGION idiom already in this file. The region runs from the "Discover with" opener to the kind-table header, exclusive, so an insertion anywhere in the prose — including just past the draft_id sentence, where the attack landed — falls inside it. A region ending at that sentence would leave the evasion one character outside and change nothing. The per-rule assertions stay and run first, so a broken rule still reports itself by name; the region is the catch-all for what none of them can see. Ordered the other way it fires first on every in-region edit and the precise messages never surface. This reddens on every upstream reword of the paragraph, harmless ones included. That is intended: the text tells an agent when a call may fire an external write without asking, so a human should read each change before it reaches users. The comment records how to update it correctly, since the tempting fix is to loosen it back to a substring. Mutation-proved, each case also resealing the manifest so the digest gate cannot mask the result: the review's exact insertion, the same contradiction appended at the end of the block, a deleted line, a one-word weakening, a pure reflow, and the previous round's five cases all turn the suite red. --- tests/plugin-contract/run.ts | 70 ++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/tests/plugin-contract/run.ts b/tests/plugin-contract/run.ts index e70fb50..0b9d9ba 100644 --- a/tests/plugin-contract/run.ts +++ b/tests/plugin-contract/run.ts @@ -1602,6 +1602,62 @@ for (const dir of skillDirs) { ); } +// The whole rendered action contract, pinned verbatim. The `includes` assertions +// below stay — they name WHICH rule broke, which "the paragraph changed" cannot — +// but presence is not enough on its own: `includes` proves a sentence is there and +// says nothing about what sits NEXT to it. Demonstrated during review: leaving +// every pinned sentence intact and INSERTING "the setting is off for almost +// everyone, so plan on the draft" straight after the `draft_id` rule kept every +// substring check green while reversing what the contract tells an agent to do. +// Only whole-region equality refuses an addition. +// +// BOUNDARIES. Start: the `Discover with …` line that opens the contract. End: the +// `| \`kind\` |` table header, EXCLUSIVE — so the region covers the entire prose +// block and an insertion anywhere in it, including just past the `draft_id` +// sentence where the review's attack landed, falls INSIDE the pin. Stopping at the +// `draft_id` sentence would leave that exact evasion one character outside the +// region and change nothing. Both anchors are structural rather than editorial: +// the opener is the first line every rendered face of this contract starts with, +// and the table header is a fixed column row, so neither moves under a reword of +// the prose between them. +// +// COST, accepted deliberately: this reddens on EVERY upstream reword of the +// paragraph, including harmless ones. That is the point — this text tells an agent +// when a call may fire an external write without asking, so a human should have to +// read each change to it before it ships to users. +// +// TO UPDATE (the only correct procedure): read the upstream diff and satisfy +// yourself the new wording is true of what the server does, then replace this +// constant with the new bytes. Do NOT relax it back to a substring or trim the +// region to make a sync green — that reintroduces the additive hole above. +const WRITE_ACTION_CONTRACT_REGION = [ + "Discover with `query type=integration_action text=\"\"`.", + "It returns slug/title/snippet, so call `get_page` on the selected", + "slug to read frontmatter. Use its `install_id`, `action_name`, and structured", + "`input` in `act_on_integration`; this tuple is the only call", + "shape. Partial tuples error; only bare legacy `source` returns `clarification`.", + "`requires_confirmation:true` normally drafts for out-of-band approval;", + "`requires_confirmation:false` runs inline. A user may additionally enable direct", + "execution in their brains settings, in which case a short allowlist of low-risk", + "actions also runs inline when called from an eligible client signed in from the", + "user's own computer (CLI clients such as Claude Code and Codex) — every execution", + "is still recorded in /inbox. The mode is decided server-side per call, so treat", + "any call as potentially executing and never assume a `draft_id`. If", + "`requires_confirmation` is absent, the page predates the field: treat whether", + "it drafts or runs as unknown. `side_effect` says where it writes", + "(`external` = the provider, visible outside brains;", + "`null` or absent = undeclared, treat as external). Inline external writes", + "include `rsvp_event`,", + "`create_draft`, and `add_labels`; do not infer safety from read vs write.", + "Cap: 30 auto-executions/install/60s.", + "Automation `dry_run` suppresses external writes to no-call `[DRY RUN]` drafts.", +].join("\n"); +// Asserted AFTER the per-rule checks below, deliberately: they run first and name +// the specific rule that broke, and this one is the catch-all for everything they +// cannot see — an addition, a reflow, a sentence nobody thought to pin. Ordered the +// other way it fires first on every in-region edit and the precise messages never +// surface, which is the whole reason for keeping them. + // Keep independent semantic assertions: digest equality proves provenance, not // that the canonical source itself kept the load-bearing safety rules. assert( @@ -1680,6 +1736,20 @@ assert(writeSkillNormalized.includes("remains approvable"), "expired drafts must assert(writeSkillNormalized.includes("this tuple is the only call shape"), "source-only action fallback must stay prohibited"); assert(!/act_on_integration[^.]{0,200}request=/.test(writeSkillNormalized), "free-form action request must not return"); +// The catch-all for the contract paragraph — see WRITE_ACTION_CONTRACT_REGION above. +const writeContractStart = writeSkill.indexOf("Discover with `query type=integration_action"); +const writeContractEnd = writeSkill.indexOf("| `kind` | What happened |"); +assert(writeContractStart > 0, "brains-write must keep its action-contract opener"); +assert( + writeContractEnd > writeContractStart, + "brains-write's action contract must precede the kind table — the slice below depends on it", +); +assert( + normalizeRegion(writeSkill.slice(writeContractStart, writeContractEnd)) === + WRITE_ACTION_CONTRACT_REGION, + "brains-write's action contract must match the approved copy exactly — an inserted sentence can contradict a rule the per-rule checks above still find (regenerate upstream, re-read the change, then update WRITE_ACTION_CONTRACT_REGION)", +); + // Same treatment for brains-automation, and for the same reason: the digest proves // these bytes came from the catalog, not that the catalog kept the rails. Nothing // here would notice if a regeneration DELETED the secret-hygiene guidance — the