@vibes.diy/prompts 11.1.1 → 11.1.2

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 CHANGED
@@ -43,6 +43,58 @@ type AccessDescriptor = {
43
43
 
44
44
  The question that decides which one a doc wants is whose thing it is: **personal content belongs to the PERSON; a contribution to a shared artifact belongs to the PLACE.** Someone's picks, favorites, streaks, or profile items are the person's — give them an `audience` arm keyed on their own handle, so the reach tracks that person's settings and follows them everywhere. Guestbook entries, posts on a communal wall, items on a shared board are the place's — route them to the place's channel with `grant.public` or explicit grants exactly as the rest of this doc shows, unaffected by the author's later profile flips: a guestbook entry doesn't vanish because the signer went private.
45
45
 
46
+ ### Two controls: a followers checkbox and an individual buddy picker
47
+
48
+ 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.
49
+
50
+ 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.
51
+ 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.
52
+
53
+ 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.
54
+
55
+ App.jsx — the checkbox is the consent call; the picker is a doc write:
56
+
57
+ ```jsx
58
+ // checking is the consent preflight; unchecking is the off switch. Never one verb for both edges.
59
+ <label>
60
+ <input
61
+ type="checkbox"
62
+ checked={followersEnabled}
63
+ onChange={(e) => (e.target.checked ? requestFollowersAccess() : setVisibility("private"))}
64
+ />
65
+ Shared with{" "}
66
+ <button type="button" onClick={() => setShowFollowers(true)}>
67
+ followers
68
+ </button>
69
+ </label>
70
+ {showFollowers && <ul>{followers.map((f) => <li key={f.handle}>{f.handle}</li>)}</ul>}
71
+
72
+ {/* the app's own list — one share doc per person picked */}
73
+ {can.create({ type: "share", authorHandle: me?.userHandle, buddyHandle: "x" }).ok && <AddBuddyForm />}
74
+ ```
75
+
76
+ access.js — the buddy grant and the followers audience arm are independent:
77
+
78
+ ```js
79
+ const ch = `log:${user.userHandle}`;
80
+
81
+ if (doc.type === "share") {
82
+ // app-owned: grant the ONE handle this person picked (plus themselves).
83
+ if (doc.authorHandle !== user.userHandle) throw { forbidden: "your own log" };
84
+ return { channels: [ch], grant: { users: { [doc.buddyHandle]: [ch], [user.userHandle]: [ch] } } };
85
+ }
86
+
87
+ if (doc.type === "entry") {
88
+ // platform-owned: the audience arm; the checkbox above is what arms it.
89
+ if (doc.authorHandle !== user.userHandle) throw { forbidden: "not author" };
90
+ return {
91
+ channels: [ch],
92
+ grant: { users: { [user.userHandle]: [ch] } },
93
+ audience: { followersOf: user.userHandle },
94
+ };
95
+ }
96
+ ```
97
+
46
98
  ### The ImgGen version-append nuance
47
99
 
48
100
  The platform predicate `ctx.isImgGenVersionAppend(doc, oldDoc)` accepts exactly one legitimate ImgGen version append by a non-author — prior versions/files and all other fields must be unchanged, so a field you didn't anticipate stays author-protected by default; deletes stay author-only. Because `versions`/`currentVersion` are among the version fields it allows, a non-author can also advance the DISPLAYED version (the shared-generation feature); guard `currentVersion` specifically for author-only display control. Write-once docs can simply `if (oldDoc === null) {} else throw`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibes.diy/prompts",
3
- "version": "11.1.1",
3
+ "version": "11.1.2",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "description": "",
@@ -24,9 +24,9 @@
24
24
  "license": "Apache-2.0",
25
25
  "dependencies": {
26
26
  "@adviser/cement": "~0.5.34",
27
- "@vibes.diy/call-ai-v2": "^11.1.1",
28
- "@vibes.diy/identity": "^11.1.1",
29
- "@vibes.diy/use-vibes-types": "^11.1.1",
27
+ "@vibes.diy/call-ai-v2": "^11.1.2",
28
+ "@vibes.diy/identity": "^11.1.2",
29
+ "@vibes.diy/use-vibes-types": "^11.1.2",
30
30
  "arktype": "~2.2.3",
31
31
  "json-schema-faker": "~0.6.2"
32
32
  },
package/system-prompt.md CHANGED
@@ -467,6 +467,18 @@ docs in your database, and never build follow UI state machines.
467
467
  app is already enabled, or the viewer already declined/opted out) — never call it in a loop,
468
468
  on every render, or more than once per explicit user action. Don't invent a callout an app's
469
469
  own privacy doesn't call for.
470
+ - **A privacy-sensitive app's sharing surface is TWO visually distinct controls, because they are
471
+ two mechanisms.** (1) A "Shared with followers" checkbox — the PLATFORM's follower list. It starts
472
+ unchecked; checking it is the consent, so call `requestFollowersAccess()` from that change handler
473
+ and never on mount — and branch on the new checked state in that same handler so UNchecking calls
474
+ `setVisibility("private")`, or the box is an enable-only button that can't be undone. Make the
475
+ word "followers" tappable to preview the viewer's real
476
+ `followers` list, so it reads as the app-independent list it already is. (2) A separately labelled
477
+ individual picker named for the app's domain ("add workout buddies") — the APP's own list, where
478
+ each person picked is granted by handle in `access.js`. Keep them visually distinct, never merge
479
+ them into one toggle, and never let one write the other's state; removal copy on either is
480
+ "stops receiving new data". The grant half and the worked `access.js`/JSX snippets live in the
481
+ `access` skill.
470
482
  - For an explicit visibility control (a settings toggle inside your app), `useSocial()` returns
471
483
  `visibility: "public" | "followers" | "private"` (read-only: the effective level for THIS
472
484
  viewer/app — `followersEnabled` is just `visibility !== "private"`) and