@zibby/skills 2.0.4 → 2.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,163 @@
1
+ ---
2
+ sidebar_position: 7
3
+ title: sidecars & sidecarSpecs
4
+ ---
5
+
6
+ # `sidecarSpecs` and `sidecars`
7
+
8
+ ## What it is
9
+
10
+ Some agents need a long-lived service running beside them: a browser, a vector
11
+ database, a bridge that turns a REST API into tools. Those are **sidecars**.
12
+
13
+ `sidecarSpecs` carries the **whole specification** — name, a checksum-pinned
14
+ image on Zibby's own CDN, the port, the per-tenant config it reads — rather than
15
+ a name the platform must already know. That is what makes a brand-new
16
+ sidecar-backed agent *a template and nothing else*.
17
+
18
+ `sidecars` is the short form: a name for a sidecar the platform already ships.
19
+
20
+ At deploy the control plane fetches the image, **verifies its SHA-256**, loads it
21
+ and starts it on the internal network. The agent's run container never pulls
22
+ anything and never dials it directly.
23
+
24
+ :::caution Self-hosted only
25
+ Sidecar hosting is not built on Zibby Cloud yet. A template that declares one is
26
+ marked self-hosted-only and the cloud catalog will not deploy it. There is one
27
+ exception, [below](#the-one-cloud-exception).
28
+ :::
29
+
30
+ ## Syntax
31
+
32
+ ```js
33
+ spec: {
34
+ selfHostOnly: true,
35
+ sidecarSpecs: [{
36
+ name: 'openapi-mcp',
37
+ version: '0.2.3',
38
+ s3Url: 'https://dl.zibby.app/sidecars/openapi-mcp/0.2.3.tar.gz',
39
+ sha256: 'caa4ebfc04acf725f3afd0afa8d0ddf942a600eea364ec821351e3f19af53b4f',
40
+ bytes: 2500336, // informational — shown in the dialog
41
+ arch: {
42
+ arm64: {
43
+ s3Url: 'https://dl.zibby.app/sidecars/openapi-mcp/0.2.3-arm64.tar.gz',
44
+ sha256: '38fe9ca08961eed8c2768e3d036da6deaf4ca2384543ea243deb9685a77ca566',
45
+ bytes: 2305415,
46
+ },
47
+ },
48
+ port: 8080,
49
+ healthPath: '/health',
50
+ warm: true,
51
+ requestConfigKeys: ['OPENAPI_APIS', 'API_ROOT', 'API_TOKEN'],
52
+ }],
53
+ // NO `sidecars: ['openapi-mcp']` — a full spec implies its own name.
54
+ }
55
+ ```
56
+
57
+ ## Properties
58
+
59
+ | Name | Type | Required | Allowed values | Default | Update behavior |
60
+ |---|---|---|---|---|---|
61
+ | `name` | string | **Yes** | lowercase, starts with a letter, letters/digits/hyphens, 2–31 chars. May not shadow a built-in name | — | Re-applied |
62
+ | `port` | integer | **Yes** | 1–65535 | — | Re-applied |
63
+ | `sha256` | string | **Yes** | 64 hex characters | — | Re-applied — a new checksum pulls a new image |
64
+ | `s3Url` | string | **Yes** | an allow-listed host | — | Re-applied |
65
+ | `version` | string | No | a version string you own | none | Re-applied; shown in the Update dialog |
66
+ | `arch.<arch>` | object | No | `{ s3Url, sha256, bytes }`, validated identically | none | Re-applied |
67
+ | `healthPath` | string | No | a path | `/health` | Re-applied |
68
+ | `warm` | boolean | No | | `false` | Re-applied |
69
+ | `bytes` | integer | No | **informational only** — never checked against the download | none | Card only |
70
+ | `dataPath` | string | No | an absolute path — and it must be `/data` | none | Re-applied |
71
+ | `dataPurpose` | string | No | `data`, `credentials` | `data` | Re-applied — decides what uninstall deletes |
72
+ | `requestConfigKeys` | string[] | No | Env keys read **per request** from the declaring agent's own encrypted bag | `[]` | Re-applied |
73
+ | `envKeys` | string[] | No | container-wide environment | `[]` | Re-applied |
74
+ | `publicPaths` | string[] | No | prefixes exposed **anonymously** on your box's public origin — refused by default | none | Re-applied |
75
+ | `storeTypes` | string[] | No | store types this sidecar serves | `[]` | Re-applied |
76
+ | `instancePerAgent` | boolean | No | | `false` | Re-applied |
77
+ | `memoryBytes` / `pidsLimit` | integer | No | 64 MiB – 8 GiB / 16 – 4096 | platform defaults | Re-applied |
78
+
79
+ `sidecars` is simply `string[]`. A name nobody knows is skipped with a warning,
80
+ not an error.
81
+
82
+ ## One shared service, many isolated tenants
83
+
84
+ A sidecar is **one process hosting N isolated tenants**, not one container per
85
+ agent. Everything that differs per agent — data *and* configuration — is
86
+ isolated:
87
+
88
+ - **Data** is keyed by a server-derived tenant key, so different tenants run in
89
+ parallel and the same tenant serialises.
90
+ - **Configuration and secrets** come from `requestConfigKeys`, injected **per
91
+ request** from the declaring agent's encrypted Env bag — never as
92
+ container-wide environment.
93
+
94
+ That last point is the one people get wrong. Putting an OAuth client id and
95
+ secret in `envKeys` means two projects with different apps collide, and the fix
96
+ is not "error on conflict" — it is per-request injection.
97
+
98
+ ## Gotchas
99
+
100
+ :::danger A full spec implies its name
101
+ If you declare `sidecarSpecs`, do **not** also list that name in `sidecars`.
102
+ The name is taken from the spec. Repeating it is a second place to keep in sync.
103
+ :::
104
+
105
+ :::danger Durable state lives at `/data` — one path, every sidecar
106
+ The `dataPath` you declare and the path your app actually writes must be the
107
+ **same string**. Nothing cross-checks them, so a mismatch is silent: the data
108
+ lands in the container's throwaway layer and dies with it, while the volume you
109
+ declared sits empty. A live OAuth token store was lost exactly that way. Do not
110
+ "fix" a mismatch by injecting a path variable — change one of them.
111
+ :::
112
+
113
+ **A checksum pins bytes, not a CPU.** There is deliberately no fallback between
114
+ architectures: an x86 image verifies perfectly on an ARM box and then dies with
115
+ `exec format error`. Publish and pin each architecture, or that architecture
116
+ fails loudly.
117
+
118
+ **A URL with no checksum fails closed.** The image is never loaded unverified.
119
+
120
+ **A template-carried sidecar does not auto-update.** Its version belongs to you:
121
+ it moves when your `version`/`sha256`/`s3Url` move and the user takes the
122
+ Update. Platform-shipped sidecars follow a CDN channel instead.
123
+
124
+ :::danger A sidecar with no authentication of its own must not declare `publicPaths`
125
+ `publicPaths` exposes those prefixes **anonymously** on your box's public
126
+ origin, with no platform authentication added. Agents and the chat assistant
127
+ reach a sidecar over the internal network and do not need it.
128
+
129
+ Declare an [`entryPoints.mcp`](./surfaces.md) with `auth: 'bearer-pat'` instead,
130
+ and let the platform's own authenticated endpoint forward to you — it adds the
131
+ token check, the ownership check, and per-request tenant configuration.
132
+
133
+ It also costs you cloud deployability. See below.
134
+ :::
135
+
136
+ **`warm: true` is sometimes required, not an optimisation.** A sidecar is
137
+ started by the boot pre-warmer, by a public-path proxy, or by the store router.
138
+ An agent that is dialled directly as an MCP server matches none of those, so
139
+ without `warm` it would never be running.
140
+
141
+ **Share a spec, do not copy it.** If several templates use the same sidecar,
142
+ import the spec from one place. Two inlined copies at different versions will
143
+ fight, and the last deploy wins — which can silently downgrade the shared engine.
144
+
145
+ ## The one cloud exception
146
+
147
+ A self-hosted-only template **is** deployable on Zibby Cloud when it is
148
+ browser-host-servable — judged from the declaration, never from the name:
149
+
150
+ - every carried spec is fully pinned (`name`, `version`, `port`, `s3Url`,
151
+ `sha256`), **and**
152
+ - no spec declares `publicPaths`, **and**
153
+ - the template declares an `entryPoints.mcp` pointing at one of its sidecars.
154
+
155
+ One `publicPaths` entry costs the template its cloud deployability.
156
+
157
+ ## See also
158
+
159
+ - [`surfaces` and `entryPoints`](./surfaces.md)
160
+ - [`stores`](./stores.md) — a store type brings its own sidecar; do not name it
161
+ twice.
162
+ - [Bring your own sidecar](../self-host/custom-sidecars.md) — the operator route,
163
+ for a service that is not a catalog agent.
@@ -0,0 +1,113 @@
1
+ ---
2
+ sidebar_position: 3
3
+ title: stores
4
+ ---
5
+
6
+ # `stores`
7
+
8
+ ## What it is
9
+
10
+ Durable storage the agent needs. Declared **on a node**, not in the
11
+ `spec` block.
12
+
13
+ At deploy the platform creates (or re-uses) one store per declaration and writes
14
+ its id into the agent's encrypted Env bag under the name you chose. Your code
15
+ never sees an id in the template — it asks for the name and gets the resource.
16
+
17
+ This is the reference implementation of the naming rule: declare a name, the
18
+ platform binds it, the consumer resolves it late.
19
+
20
+ ## Syntax
21
+
22
+ ```js
23
+ // nodes/ingest-node.js
24
+ import { SKILLS } from '@zibby/core';
25
+
26
+ export const ingestNode = {
27
+ name: 'ingest',
28
+ skills: [SKILLS.DATASET_STORE], // required — see gotchas
29
+ stores: [
30
+ { name: 'knowledge_docs', type: 'docs', description: 'Verbatim markdown archive' },
31
+ { name: 'knowledge_meta', type: 'sqlite', description: 'Per-document metadata',
32
+ schema: {
33
+ tables: {
34
+ knowledge_meta: {
35
+ ddl: 'CREATE TABLE IF NOT EXISTS knowledge_meta (id TEXT PRIMARY KEY, title TEXT)',
36
+ },
37
+ },
38
+ },
39
+ },
40
+ ],
41
+ };
42
+ ```
43
+
44
+ The minimal form is just a name and a description:
45
+
46
+ ```js
47
+ stores: [{ name: 'scorecards', description: 'Per-commit engineering scorecard' }],
48
+ ```
49
+
50
+ ## Properties
51
+
52
+ | Name | Type | Required | Allowed values | Default | Update behavior |
53
+ |---|---|---|---|---|---|
54
+ | `name` | string | **Yes** | lowercase, starts with a letter, letters/digits/underscore, ≤ 41 chars. **Unique across the whole agent**, not just the node | — | Re-applied (idempotent). A **new** name creates a **new** store; renaming leaves the old one behind |
55
+ | `type` | string | No | `dataset`, `sqlite`, `file`, `docs`, `postgres` (self-hosted only) | `dataset` | Re-applied. An unknown type fails the deploy loudly |
56
+ | `description` | string | No | any — it is what the agent's store catalogue shows the model | none | Re-applied |
57
+ | `schema` | object | No | `{ tables: { <name>: { ddl } } }` — **`sqlite` only**. Each `ddl` must begin `CREATE TABLE IF NOT EXISTS` and the table it creates must match the key | none | Re-applied, but an existing table is never altered |
58
+
59
+ ## Store types
60
+
61
+ | Type | Use it for |
62
+ |---|---|
63
+ | `dataset` | append-and-query records — the default |
64
+ | `sqlite` | relational data with a schema you declare |
65
+ | `file` | opaque blobs |
66
+ | `docs` | a verbatim document archive |
67
+ | `postgres` | a vector knowledge base (self-hosted only) |
68
+
69
+ ## What deploy does
70
+
71
+ 1. Computes a stable key from the agent, the node and the name, so re-deploying
72
+ re-uses the same store instead of creating another one.
73
+ 2. Creates the store if it does not exist.
74
+ 3. Writes `ZIBBY_STORE__<name> = <store id>` into the encrypted Env bag.
75
+ 4. Creates any declared SQLite tables.
76
+ 5. Seeds the node's store catalogue so the model can see what it has.
77
+
78
+ At run time the store skill turns those environment entries back into names, and
79
+ sub-graph children inherit them.
80
+
81
+ ## Gotchas
82
+
83
+ :::danger A `stores` block on a node with no store skill is silently ignored
84
+ The same node must also declare a store skill (`dataset-store`, or `gbrain` for
85
+ a vector KB) in its **`skills`** array. Putting it in `optionalSkills` instead
86
+ provisions nothing — no store, no error, no warning.
87
+ :::
88
+
89
+ **Names are unique per agent, not per node.** Two nodes cannot each declare a
90
+ store called `results`.
91
+
92
+ **If you re-point a store by hand, that wins forever.** Setting
93
+ `ZIBBY_STORE__<name>` yourself to something other than the provisioned id tells
94
+ the platform this is deliberate: it stops re-binding the name and stops applying
95
+ the declared schema.
96
+
97
+ **A schema on a non-SQLite store is a hard error at deploy**, not a warning.
98
+
99
+ **Declared tables are created, never migrated.** If a table already exists it is
100
+ left exactly as it is, even when your `ddl` has changed. Adding a column is your
101
+ migration to write.
102
+
103
+ **A store type brings its own service.** `postgres` pulls the vector-KB engine
104
+ by itself — do not also name it under [`sidecars`](./sidecars.md).
105
+
106
+ **`postgres` cannot be deployed on Zibby Cloud today.** Templates that need it
107
+ are marked self-hosted-only.
108
+
109
+ ## See also
110
+
111
+ - [Node declarations](./node-declarations.md) — the store skill is a
112
+ precondition, not a nicety.
113
+ - [Sidecars](./sidecars.md)
@@ -0,0 +1,184 @@
1
+ ---
2
+ sidebar_position: 6
3
+ title: surfaces & entryPoints
4
+ ---
5
+
6
+ # `surfaces` and `entryPoints`
7
+
8
+ Two blocks, one subject: **which channels your agent offers, and where each one
9
+ lives.**
10
+
11
+ - `surfaces` — *which* channels the agent's page should show.
12
+ - `entryPoints` — *where* a channel lives when it is not served by the platform
13
+ itself, and every word the card shows.
14
+
15
+ ---
16
+
17
+ ## `surfaces`
18
+
19
+ ### What it is
20
+
21
+ The deployed agent's page renders one section per interaction channel.
22
+ `surfaces` says which of them are meaningful, so a pure knowledge-base agent
23
+ does not show a Trigger button that does nothing.
24
+
25
+ ### Syntax
26
+
27
+ ```js
28
+ spec: { surfaces: ['mcp', 'connect'] }
29
+ ```
30
+
31
+ ### Properties
32
+
33
+ | Name | Type | Required | Allowed values | Default | Update behavior |
34
+ |---|---|---|---|---|---|
35
+ | `surfaces` | string[] | No | `cli`, `webhook`, `schedule`, `mcp`, `connect` | `['cli','webhook','schedule','mcp']` | Re-applied |
36
+
37
+ | Value | Meaning |
38
+ |---|---|
39
+ | `cli` | started from the command line |
40
+ | `webhook` | started by an inbound HTTP call |
41
+ | `schedule` | started by cron |
42
+ | `mcp` | a machine endpoint an MCP client attaches to |
43
+ | `connect` | a public page a **human opens in a browser** to link their own account — an OAuth sign-in, a device pairing, a licence activation |
44
+
45
+ `connect` is not a webhook: nothing third-party posts to it and it starts no run.
46
+
47
+ Unknown values are ignored. Declaring nothing gives you the four standard
48
+ channels — **not** all five, because `connect` is opt-in by design.
49
+
50
+ ### Use cases
51
+
52
+ | Agent shape | Declare |
53
+ |---|---|
54
+ | An ordinary triggerable workflow | nothing |
55
+ | A pure store — running it directly is a no-op, it is driven over MCP | `['mcp']` |
56
+ | A service a person signs into, then uses from their editor | `['mcp', 'connect']` |
57
+
58
+ ### Gotchas
59
+
60
+ Declaring `mcp` does not create an endpoint — it says the agent has one. The
61
+ endpoint still has to exist.
62
+
63
+ ---
64
+
65
+ ## `entryPoints`
66
+
67
+ ### What it is
68
+
69
+ For a channel that need not be served by the platform, `entryPoints` names which
70
+ service serves it and at which path. **The URL itself is derived when the page
71
+ is read** — from the service's resolved public mount and your installation's
72
+ origin. No template ever writes a URL down.
73
+
74
+ For `connect` it also carries every word on the card, because the renderer has
75
+ none of its own.
76
+
77
+ ### Syntax
78
+
79
+ An MCP endpoint the platform authenticates and forwards to a service:
80
+
81
+ ```js
82
+ spec: {
83
+ surfaces: ['mcp'],
84
+ entryPoints: {
85
+ mcp: {
86
+ sidecar: 'openapi-mcp',
87
+ path: '/mcp',
88
+ auth: 'bearer-pat',
89
+ label: 'MCP endpoint for the APIs this agent bridges',
90
+ },
91
+ },
92
+ }
93
+ ```
94
+
95
+ A sign-in the platform drives, on an agent with no service container at all:
96
+
97
+ ```js
98
+ spec: {
99
+ surfaces: ['mcp', 'connect'],
100
+ entryPoints: {
101
+ connect: {
102
+ kind: 'oauth',
103
+ server: 'figma', // a name from this agent's own remoteMcp block
104
+ label: 'Connect your Figma account',
105
+ button: 'Connect',
106
+ buttonDisconnect: 'Disconnect',
107
+ disconnectConfirm: 'Disconnect Figma? The stored sign-in is deleted.',
108
+ connectedLine: 'Connected to Figma as {account}.',
109
+ },
110
+ // NO `mcp` entry — the absence IS the declaration. See below.
111
+ },
112
+ }
113
+ ```
114
+
115
+ ### Properties
116
+
117
+ Only `mcp` and `connect` can be redirected. `cli`, `webhook` and `schedule` are
118
+ platform mechanics with no alternative home.
119
+
120
+ | Name | Type | Required | Allowed values | Default | Update behavior |
121
+ |---|---|---|---|---|---|
122
+ | `sidecar` | string | **Yes**, except for `connect` with `kind: 'oauth'` | a service this agent declares, or one the platform ships | — (entry is dropped without it) | Re-applied |
123
+ | `path` | string | **Yes**, same exception | must start with `/`; `..` is refused | — | Re-applied |
124
+ | `auth` | string | No | `bearer-pat`, `connect-bearer`, `none` | `connect-bearer` for `mcp`, `none` for `connect` | Re-applied |
125
+ | `label` | string | No | any | none | Card only |
126
+ | `kind` (connect) | string | No | `sidecar-page`, `oauth` | `sidecar-page` | Re-applied |
127
+ | `server` (connect + `oauth`) | string | **Yes** for that kind | a name from `remoteMcp` | — | Re-applied |
128
+ | copy (`button`, `buttonDisconnect`, `disconnectConfirm`, `disconnectedLine`, `waitingLine`, `connectedLine`, `note`) | string | No | `{account}` is substituted | none — a field you omit renders nothing | Card only |
129
+
130
+ ### `auth` names what the CALLER presents
131
+
132
+ | Value | The caller presents |
133
+ |---|---|
134
+ | `bearer-pat` | a Zibby access token — the platform authenticates and forwards |
135
+ | `connect-bearer` | the token this agent's own sign-in handed the user |
136
+ | `none` | nothing — the surface authenticates the caller itself |
137
+
138
+ :::warning A misspelt `auth` is silently corrected, not rejected
139
+ An unrecognised value falls back to the channel's default, so a typo quietly
140
+ describes an authentication your endpoint does not use. Spell it exactly.
141
+ :::
142
+
143
+ ### An absent `entryPoints.mcp` is itself a declaration
144
+
145
+ Leave it out and **the platform serves the surface itself**, at the agent's own
146
+ authenticated MCP address, with a Zibby bearer token.
147
+
148
+ That is the right choice whenever your service has no authentication of its own.
149
+ Naming a source when you did not need to is how an endpoint gets taken away from
150
+ the sign-in that feeds it: the declared service wins the address and answers
151
+ every call with its own "missing token".
152
+
153
+ :::tip One agent, one MCP address
154
+ A declared MCP service takes the **whole** surface — other MCP servers attached
155
+ to the same agent are not merged into it. If you need both, you need two agents.
156
+ :::
157
+
158
+ ### Use cases
159
+
160
+ | You want | Declare |
161
+ |---|---|
162
+ | Platform-served MCP with a Zibby token | `surfaces: ['mcp']` and nothing else |
163
+ | MCP forwarded to your own service, platform-authenticated | `entryPoints.mcp` with `auth: 'bearer-pat'` |
164
+ | A service that runs its own OAuth and issues its own bearer | `connect` with `auth: 'none'` **and** `mcp` with `auth: 'connect-bearer'` |
165
+ | A hosted third-party MCP the platform signs you into | `connect` with `kind: 'oauth'`, and **no** `mcp` entry |
166
+
167
+ ### Gotchas
168
+
169
+ **Both halves are required.** A channel must be in `surfaces` *and* have an
170
+ `entryPoints` entry before anything is derived.
171
+
172
+ **A path the proxy would not serve is never advertised.** If the mount does not
173
+ publish it, the entry is dropped rather than shown as a dead link.
174
+
175
+ **`connect-bearer` turns off the platform's own token check** on the agent's MCP
176
+ address. Only declare it when your service really does authenticate.
177
+
178
+ **Service-served entries need a self-hosted box.** A `connect` entry with
179
+ `kind: 'oauth'` works everywhere.
180
+
181
+ ## See also
182
+
183
+ - [`sidecars`](./sidecars.md), [`remoteMcp`](./remote-mcp.md),
184
+ [`requires`](./requires.md)
@@ -0,0 +1,108 @@
1
+ ---
2
+ sidebar_position: 12
3
+ title: Update behavior
4
+ ---
5
+
6
+ # Update behavior
7
+
8
+ CloudFormation tells you, per property, whether a change updates the resource in
9
+ place or replaces it. Zibby's declaration model has the same question and four
10
+ answers. Every properties table in this section carries the column.
11
+
12
+ | Class | What happens on the next deploy |
13
+ |---|---|
14
+ | **Re-applied** | the platform re-runs the action — provision the store, register the sidecar, attach the endpoint, bind the server. Idempotent: nothing is duplicated and nothing working is broken |
15
+ | **Seeded once** | written on the **first** install only. Whatever the operator set afterwards survives every Update |
16
+ | **Card only** | reaches the catalog card and nothing else. A deployed agent never consumes it, so changing it never offers an Update |
17
+ | **Frozen** | baked into the deployed agent when it was installed. Changing it needs a re-deploy |
18
+
19
+ ---
20
+
21
+ ## Seeded once — the list
22
+
23
+ These are written on a fresh install and never again:
24
+
25
+ - [`selections`](./deploy-time-config.md#selections) — a value already set is kept, and the deploy tells you so
26
+ - [`defaultSchedule`](./deploy-time-config.md#defaultschedule) — **any** existing schedule blocks the seed, including a paused one
27
+ - [`deployInput`](./deploy-time-config.md#deployinput) values
28
+ - [`maxRuntimeMinutes`](./catalog-metadata.md)
29
+ - `defaultSlug` — an agent's name is fixed at first install
30
+
31
+ The rule behind all of them: **the platform never overwrites a decision you
32
+ made.** If your agent needs an operator to change something, say so in the
33
+ release notes; do not try to write it for them.
34
+
35
+ ---
36
+
37
+ ## Frozen — why a published fix does not reach a deployed agent
38
+
39
+ :::danger This is the one that surprises people
40
+ When you deploy an agent, the platform stores **that version's source** —
41
+ including its `package.json` and every version range in it — against the agent.
42
+ That copy is what runs, forever, until somebody re-deploys.
43
+
44
+ So publishing a fixed package **does not reach an agent that is already
45
+ running**. An agent installed against `^0.1.x` can never pick up a `0.2.x`. It
46
+ does not error; the runs simply keep failing the same way.
47
+ :::
48
+
49
+ **The fix is always a re-deploy** — take the Update on the agent's page, or
50
+ deploy the card again. There is no other route.
51
+
52
+ The same is true of the agent's `deps`, and of anything else that travels inside
53
+ the source bundle.
54
+
55
+ ---
56
+
57
+ ## What lights the "Update available" badge
58
+
59
+ Two independent signals, either of which is enough:
60
+
61
+ 1. **The template's files changed** — a checksum over your template directory.
62
+ 2. **The declaration changed** — a per-template signature over your
63
+ `spec` block, minus the fields listed as *Card only*.
64
+
65
+ The second exists because your `spec` block lives **outside** the
66
+ template directory, so a metadata-only change (a sidecar version pin, a new
67
+ `selections` picker) moves no source byte and the first signal cannot see it.
68
+
69
+ The rule the badge states, and the one to test your own field against:
70
+
71
+ > **Re-installing this agent from the current template would change this agent.**
72
+
73
+ A field is excluded from the signal only when updating provably changes nothing
74
+ for somebody already running it — tags, taglines, icons, capability bullets,
75
+ sample trigger bodies, and the release notes themselves. (Notes *describing* an
76
+ update cannot be the reason to offer one.)
77
+
78
+ :::tip Why your card is not showing an update
79
+ Two common causes:
80
+
81
+ - You changed only a *Card only* field. That is working as designed — the card
82
+ updated, nobody was offered a re-install.
83
+ - You added a new declared field to a template but the platform is not tracking
84
+ it yet. A field that is copied but not tracked only ever lands on rows written
85
+ for some other reason.
86
+ :::
87
+
88
+ ---
89
+
90
+ ## Idempotency, in practice
91
+
92
+ Everything in the *Re-applied* class is safe to run repeatedly:
93
+
94
+ | Action | On re-deploy |
95
+ |---|---|
96
+ | Store provisioning | the same store is re-used; a store you re-pointed by hand stays re-pointed |
97
+ | Sidecar registration | the same image, unless the pinned version or checksum moved |
98
+ | `requires` attachment | an existing link is kept; a stale one is replaced; one you added by hand is adopted |
99
+ | `remoteMcp` binding | matched by URL and left byte-for-byte, which is what keeps an OAuth grant alive |
100
+ | Member agents | re-deployed in place; the bindings do not churn |
101
+
102
+ And one thing that is not idempotent, by nature: **renaming**. A renamed store
103
+ is a new store; a renamed `slug` is a new agent. Nothing migrates the old one.
104
+
105
+ ## See also
106
+
107
+ - [The declaration model](./index.md)
108
+ - [Catalog metadata](./catalog-metadata.md) — release-notes rules
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zibby/skills",
3
- "version": "2.0.4",
3
+ "version": "2.0.5",
4
4
  "description": "Built-in skill definitions for the Zibby agent-workflow framework",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -116,10 +116,10 @@
116
116
  "dependencies": {
117
117
  "@modelcontextprotocol/sdk": "^1.29.0",
118
118
  "@resvg/resvg-js": "^2.6.2",
119
- "@zibby/agent-workflow": "2.0.4",
119
+ "@zibby/agent-workflow": "2.0.5",
120
120
  "@zibby/bin-oxlint": "^1.73.0",
121
121
  "@zibby/bin-semgrep": "^1.169.0",
122
- "@zibby/skill-ids": "2.0.4",
122
+ "@zibby/skill-ids": "2.0.5",
123
123
  "echarts": "^6.1.0",
124
124
  "mem0ai": "npm:@zibby/mem0ai@^3.0.5",
125
125
  "zod": "^3.23.0 || ^4.0.0"
@@ -134,7 +134,7 @@
134
134
  }
135
135
  },
136
136
  "optionalDependencies": {
137
- "@zibby/mcp-browser": "2.0.4",
137
+ "@zibby/mcp-browser": "2.0.5",
138
138
  "@zibby/mcp-memory": "*"
139
139
  },
140
140
  "devDependencies": {