@vibes.diy/prompts 14.3.53 → 14.3.55
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.
- package/llms/access.md +23 -13
- package/llms/image-gen.initial.md +5 -4
- package/llms/image-gen.md +10 -7
- package/package.json +5 -5
package/llms/access.md
CHANGED
|
@@ -34,7 +34,7 @@ writes.
|
|
|
34
34
|
|
|
35
35
|
Express every refusal about **who may act** through the supplied validators — `ctx.requireAccess`, `ctx.requireRole`, `ctx.requireBackend` — so that admin mode passes them and the function still runs, applying your own channels and grants to the repaired document; a refusal about the document's own **shape**, like an unknown type or a missing field, stays a plain `throw` and is right to, while a write-once check sits in between and is worth naming in a comment, since a schema repair has to get past it.
|
|
36
36
|
|
|
37
|
-
A component-owned image database (`media:<hostDbName>`) uses platform-authored rules by default. An explicit string-named export such as `export { posterMedia as "media:posters" }` overrides that one media database. Neither `export default` nor a function merely named `media` overrides it. Within those, platform-driven writes onto the app's own docs — like an `<ImgGen>` version append (the `ctx.isImgGenVersionAppend` shape) — flow through your function like any other write.
|
|
37
|
+
A component-owned image database (`media:<hostDbName>`) uses platform-authored rules by default: each generated image is readable by whoever can read the record it was generated for, and by the person who generated it. An explicit string-named export such as `export { posterMedia as "media:posters" }` overrides that one media database. Neither `export default` nor a function merely named `media` overrides it. Within those, platform-driven writes onto the app's own docs — like an `<ImgGen>` version append (the `ctx.isImgGenVersionAppend` shape) — flow through your function like any other write.
|
|
38
38
|
|
|
39
39
|
### AccessDescriptor return type
|
|
40
40
|
|
|
@@ -63,13 +63,15 @@ The question that decides which one a doc wants is whose thing it is: **personal
|
|
|
63
63
|
|
|
64
64
|
### Two controls: a followers checkbox and an individual buddy picker
|
|
65
65
|
|
|
66
|
-
A privacy-sensitive app — a workout log, a journal, a health or money tracker — shares nothing until the person says so, and its sharing surface is exactly **two visually distinct controls**, because they run on two different mechanisms: the PLATFORM's follower list, and the APP's own grant list.
|
|
66
|
+
A privacy-sensitive app — a workout log, a journal, a health or money tracker — shares nothing until the person says so, and its sharing surface is exactly **two visually distinct controls**, because they run on two different mechanisms: the PLATFORM's follower list, and the APP's own grant list. Build both controls in every privacy-sensitive app unless the person asked, in their own words, to keep it to themselves — a sensitive domain is the reason the controls start off, never a reason to leave them out.
|
|
67
67
|
|
|
68
68
|
1. **A "Shared with followers" checkbox** — the platform half. It starts UNCHECKED in a privacy-sensitive app and the app must never pre-check it or arm it on mount; checking the box IS the consent, so call `requestFollowersAccess()` from that one change handler and nowhere else. The box is a real two-way switch, so the same handler must branch on the new checked state and turn sharing back OFF with `setVisibility("private")` when it is unchecked — a handler that calls the consent verb on both edges is an enable-only button that can never be undone. Make the word "followers" in the label tappable and have it preview the viewer's real follower list from `useSocial().followers` — that list is platform-wide, already established, and the same in every app, which is exactly why the box cannot start checked.
|
|
69
69
|
2. **An "Add buddies" individual picker** — the app half, named for the app's own domain (workout buddies, reading buddies, a care circle). The person picks individuals by handle, and adding an individual buddy writes a share doc grant, never a change to the follow graph: the access fn returns `grant: { users: { [doc.buddyHandle]: [ch] } }` for that one chosen handle. It is follow-independent — a buddy need not follow you, adding one never checks the followers box, and this list belongs to the app.
|
|
70
70
|
|
|
71
71
|
Keep the two visually distinct (a checkbox with its own explanatory line; a separately labelled list with an add-a-handle field below it), and never let one write the other's state. Re-render both from what actually took effect — `followersEnabled` after the await, the share docs the app has written — never from what was asked for. Unchecking the box (`setVisibility("private")`) or removing a buddy means that person **stops receiving new data** — never write copy promising their existing copies are erased, or that they can no longer see what they already have.
|
|
72
72
|
|
|
73
|
+
**When the owner asks you to add publish or sharing to a privacy-sensitive app, build exactly this consent-first shape and nothing wider.** The two controls above for follower and buddy sharing, or a per-item publish behind one tap for a single finished thing (the per-item channel whose read-grant flips from a `visibility` field); never an always-on `grant.public` channel for the records themselves, never a box that starts checked or a verb called on mount, and no "Anyone" rung — the platform refuses a `"public"` level on a sensitive app, so that control would be a button that does nothing. The sensitivity mark itself is not yours to change from code: if the ask is really "this app is not sensitive", say so and point at the app's settings, where the owner makes that call.
|
|
74
|
+
|
|
73
75
|
App.jsx — the checkbox is the consent call; the picker is a doc write:
|
|
74
76
|
|
|
75
77
|
### `App.jsx`
|
|
@@ -126,10 +128,17 @@ throw { forbidden: "unknown document type" }; // every access.js ends by denying
|
|
|
126
128
|
### Private generated images and published snapshots
|
|
127
129
|
|
|
128
130
|
`<ImgGen database="posters" _id={poster._id}>` stores its generated images AND
|
|
129
|
-
prompts in `media:posters`.
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
131
|
+
prompts in `media:posters`. By default each image follows the record it was
|
|
132
|
+
generated for: whoever can read `poster` can read its images, and the person who
|
|
133
|
+
generated an image can always read it. Make the `posters` records private and
|
|
134
|
+
their images are seen only by each poster's readers and by whoever generated
|
|
135
|
+
each image; make a poster public and its images are public. No media rule is
|
|
136
|
+
needed for that.
|
|
137
|
+
|
|
138
|
+
Write a media override only when the images should be **more open or more
|
|
139
|
+
closed than their host record** — for example, drafts that only their creator
|
|
140
|
+
sees even on a shared poster. An exact string-named media export replaces the
|
|
141
|
+
default for that one media database:
|
|
133
142
|
|
|
134
143
|
```js
|
|
135
144
|
function posterMedia(doc, oldDoc, user) {
|
|
@@ -150,12 +159,13 @@ export { posterMedia as "media:posters" };
|
|
|
150
159
|
|
|
151
160
|
Each writer's versions are private even if another writer guesses the same
|
|
152
161
|
`hostDocId`; guessing never grants access to another person's image records.
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
`${me.userHandle}:${crypto.randomUUID()}`, enforce that creator prefix
|
|
157
|
-
creation, and require the same prefix on `doc.hostDocId` in the media
|
|
158
|
-
`inheritRead` controls reads; by itself it does not authorize writes to
|
|
162
|
+
An override that should keep following its host's **current** read rules
|
|
163
|
+
returns `inheritRead: { db: "posters", docId: doc.hostDocId }` — the shape the
|
|
164
|
+
default already uses. Bind writes to the intended creator too: assign host IDs
|
|
165
|
+
such as `${me.userHandle}:${crypto.randomUUID()}`, enforce that creator prefix
|
|
166
|
+
on host creation, and require the same prefix on `doc.hostDocId` in the media
|
|
167
|
+
rule. `inheritRead` controls reads; by itself it does not authorize writes to
|
|
168
|
+
the host.
|
|
159
169
|
|
|
160
170
|
To publish one finished image while keeping drafts private, read the chosen
|
|
161
171
|
version with `useImgGen`, obtain its `_files[version.id].file()`, and save that
|
|
@@ -443,7 +453,7 @@ Every item a visitor creates lands on their own `user:<handle>` channel, private
|
|
|
443
453
|
|
|
444
454
|
**When the prompt asks to share, invite, or collaborate — bring in a partner, a buddy, a friend, a team, or a group that co-edits — each shared thing becomes its own object others can be invited into.** A shopping list you invite your partner to, a board a group co-edits, a document you open to a collaborator: route that shared thing to its **own object channel** (`list:<id>`/`board:<id>`) and self-grant it at creation (`grant: { users: { [user.userHandle]: [ch] } }`), with the `authorHandle` create + `oldDoc` author checks so each item stays on its object. Then let the creator invite a chosen friend in: a `share` doc the creator authors grants that friend the same channel (`grant: { users: { [doc.invitee]: [ch] } }`), so they collaborate on that one list — sharing a single list or a whole space is the same grant at a different node of the object graph. A wall, guestbook, or map where each visitor adds their _own_ items is author-owned writes + public read: any signed-in visitor authors their own and everyone reads.
|
|
445
455
|
|
|
446
|
-
**When a user's work is private by default, show it — and offer a way to publish.** If everything a user does routes to a channel only they can read (a per-user `user:<handle>`, a private journal/notes/tracker with no `grant.public`), the UI must say so: a small, persistent "Only you can see this" / "Private to you" cue near their content, so no one wonders who's watching their unfinished work. Then, where sharing fits the app, give them a publish control — but publish at the granularity of a **channel**, not a doc: channels are the unit of read isolation, so adding `grant.public` to the shared `user:<handle>` channel would expose _every_ private item on it, not just the one they meant to share. To publish a single item, route it to its **own** channel and flip only that channel's read-grant from a `visibility` field the access fn reads — exactly the per-item-channel shape the worked example below uses (`const ch = \`entry:${doc._id}\`; const grant = { users: { [user.userHandle]: [ch] } }; if (doc.visibility === "public") grant.public = [ch];`) — for anonymous visitors, or grant a shared app channel for all granted members. Gate the control on `useVibe(dbName).can`, and reflect the result back in the affordance ("Published — anyone can see this", with an unpublish to flip it back). Making the user's _whole_ space public is fine when that's the intent; silently leaking their other private items by publishing one is the trap to avoid. **If the
|
|
456
|
+
**When a user's work is private by default, show it — and offer a way to publish.** If everything a user does routes to a channel only they can read (a per-user `user:<handle>`, a private journal/notes/tracker with no `grant.public`), the UI must say so: a small, persistent "Only you can see this" / "Private to you" cue near their content, so no one wonders who's watching their unfinished work. Then, where sharing fits the app, give them a publish control — but publish at the granularity of a **channel**, not a doc: channels are the unit of read isolation, so adding `grant.public` to the shared `user:<handle>` channel would expose _every_ private item on it, not just the one they meant to share. To publish a single item, route it to its **own** channel and flip only that channel's read-grant from a `visibility` field the access fn reads — exactly the per-item-channel shape the worked example below uses (`const ch = \`entry:${doc._id}\`; const grant = { users: { [user.userHandle]: [ch] } }; if (doc.visibility === "public") grant.public = [ch];`) — for anonymous visitors, or grant a shared app channel for all granted members. Gate the control on `useVibe(dbName).can`, and reflect the result back in the affordance ("Published — anyone can see this", with an unpublish to flip it back). Making the user's _whole_ space public is fine when that's the intent; silently leaking their other private items by publishing one is the trap to avoid. **If the person asked, in their own words, to keep it to themselves — "only I", "just me", "nobody else" — do not add publish/share controls.** A privacy-sensitive domain on its own (a journal, a health tracker) is not that ask: it gets the two controls from _Two controls_ above, off until the person says so. Keep publish opt-in: for only-me apps, omit the publish UI entirely; for share-capable apps, make publish one tap for user-selected items. **Every app that is neither privacy-sensitive nor an only-me ask ships with a sharing path already in it: either an always-on shared channel its records land on, or a publish control.** **An open-nature app — an image or meme wall, a bingo card, a party game — puts its records on an always-on `grant.public` channel from the first rules it ships, builds no publish step, and admits anonymous readers**, so everyone with the link sees what people make the moment they make it. The always-on channel is the least-surprise shape for anything playful and low-stakes (an image or meme wall, a group list, a guestbook): a `grant.public` channel, a members' channel, or an `audience` arm, so a thing someone makes is seen the moment they make it. The publish control is the shape for work people draft first: the per-item channel whose read-grant flips from a `visibility` field, behind one tap. A publish control that names its destination — "Publish to the wall", "Share to the group" — is the best shape of the second, not a requirement. An ordinary app with neither is a bug: every record lands on a channel only its writer can read, and nobody asked for that.
|
|
447
457
|
|
|
448
458
|
**Worked example — a shared catalog people track against, with per-thing visibility (a social habit app, a reading challenge, a fitness ladder).** The catalog items are public objects anyone proposes; each person's progress is their own; and each person chooses — _once per item, never per entry_ — whether their streak is public (on the leaderboard) or buddy-only. The visibility choice lives on a per-`(person, item)` **tracking** record that sets the read-grant the entries routed to it inherit.
|
|
449
459
|
|
|
@@ -208,10 +208,11 @@ Model ids follow the `provider/model-name` form from the platform's model catalo
|
|
|
208
208
|
|
|
209
209
|
## Generated-image privacy
|
|
210
210
|
|
|
211
|
-
Generated images and prompts use a component-owned media database
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
211
|
+
Generated images and prompts use a component-owned media database, and by
|
|
212
|
+
default each image follows the record it was generated for: whoever can read
|
|
213
|
+
that record can read its images, and the person who generated an image always
|
|
214
|
+
can. Describe generated image history as visible to the readers of the record
|
|
215
|
+
it belongs to, plus whoever generated each image.
|
|
215
216
|
|
|
216
217
|
Publish a separate record containing only the chosen image File and the intended
|
|
217
218
|
public facts. Public viewers render its `_files` URL with `<img>`; keep generation
|
package/llms/image-gen.md
CHANGED
|
@@ -193,13 +193,16 @@ function Gallery() {
|
|
|
193
193
|
|
|
194
194
|
## Private drafts and public images
|
|
195
195
|
|
|
196
|
-
Generated images and prompts live in `media:<database>`,
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
196
|
+
Generated images and prompts live in `media:<database>`, and by default each
|
|
197
|
+
image follows the record it was generated for: whoever can read that record can
|
|
198
|
+
read its images, and the person who generated an image always can. A private
|
|
199
|
+
record's images stay with its readers and each image's creator, with no extra
|
|
200
|
+
rule. For images that should be
|
|
201
|
+
more private than their record — drafts only their creator sees on a shared
|
|
202
|
+
record — add an explicit `export { privateMedia as "media:yourDatabase" }` in
|
|
203
|
+
`access.js`, with creator-owned version/pointer rules and private read grants.
|
|
204
|
+
An app-wide default export does not override media. See the access skill's
|
|
205
|
+
"Private generated images and published snapshots" recipe.
|
|
203
206
|
|
|
204
207
|
Publish a separate record containing only the chosen image File and the intended
|
|
205
208
|
public facts. Public viewers render its `_files` URL with `<img>`; keep generation
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibes.diy/prompts",
|
|
3
|
-
"version": "14.3.
|
|
3
|
+
"version": "14.3.55",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"exports": {
|
|
@@ -37,10 +37,10 @@
|
|
|
37
37
|
"license": "Apache-2.0",
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@adviser/cement": "~0.5.34",
|
|
40
|
-
"@vibes.diy/call-ai-v2": "14.3.
|
|
41
|
-
"@vibes.diy/identity": "14.3.
|
|
42
|
-
"@vibes.diy/use-vibes-types": "14.3.
|
|
43
|
-
"arktype": "~2.2.
|
|
40
|
+
"@vibes.diy/call-ai-v2": "14.3.55",
|
|
41
|
+
"@vibes.diy/identity": "14.3.55",
|
|
42
|
+
"@vibes.diy/use-vibes-types": "14.3.55",
|
|
43
|
+
"arktype": "~2.2.6",
|
|
44
44
|
"json-schema-faker": "~0.6.3"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|