@vibes.diy/prompts 10.8.4 → 11.0.0
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 +7 -0
- package/package.json +4 -4
- package/system-prompt.md +48 -9
package/llms/access.md
CHANGED
|
@@ -31,11 +31,18 @@ type AccessDescriptor = {
|
|
|
31
31
|
roles?: Record<roleName, string[]>; // role → channel grants (reduced by union)
|
|
32
32
|
public?: string[]; // member-public read — any member, no channel grant needed
|
|
33
33
|
};
|
|
34
|
+
audience?: { followersOf: userHandle } | { mutualsOf: userHandle }; // platform follow-graph reach
|
|
34
35
|
expiry?: string | number | null; // ISO date or unix seconds
|
|
35
36
|
allowAnonymous?: boolean; // opt-in for null-user writes
|
|
36
37
|
};
|
|
37
38
|
```
|
|
38
39
|
|
|
40
|
+
`audience` is the one field resolved against the PLATFORM follow graph instead of this app's own grants: add it to an otherwise normal result, always keyed on the live writer (`audience: { followersOf: user.userHandle }`), and the platform decides at read time who that reaches. The arm you emit is only a ceiling — the subject's own visibility setting for this app picks the actual rung (off / their followers / anyone at all), and it can shrink that reach but never grow it. The full teaching — which arm, the three-rung ladder, the `useSocial()` / `useInterest()` surfaces, and the copy rules that go with them — is in the social section of the system prompt; don't re-derive it here.
|
|
41
|
+
|
|
42
|
+
### Person or place: `audience` or `grant`
|
|
43
|
+
|
|
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
|
+
|
|
39
46
|
### The ImgGen version-append nuance
|
|
40
47
|
|
|
41
48
|
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": "
|
|
3
|
+
"version": "11.0.0",
|
|
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": "^
|
|
28
|
-
"@vibes.diy/identity": "^
|
|
29
|
-
"@vibes.diy/use-vibes-types": "^
|
|
27
|
+
"@vibes.diy/call-ai-v2": "^11.0.0",
|
|
28
|
+
"@vibes.diy/identity": "^11.0.0",
|
|
29
|
+
"@vibes.diy/use-vibes-types": "^11.0.0",
|
|
30
30
|
"arktype": "~2.2.3",
|
|
31
31
|
"json-schema-faker": "~0.6.2"
|
|
32
32
|
},
|
package/system-prompt.md
CHANGED
|
@@ -431,8 +431,28 @@ docs in your database, and never build follow UI state machines.
|
|
|
431
431
|
channel and let the item's own audience carry it, keeping each `followersOf`/`mutualsOf` subject
|
|
432
432
|
`user.userHandle`. Keep at least one real channel; keep PRIVATE data
|
|
433
433
|
on channels+grant without `audience`; never write channel names starting with `~`.
|
|
434
|
+
- **`followersOf` is a CEILING, not the audience.** The arm your access function emits says who this
|
|
435
|
+
doc may reach at most; WHO actually receives it is the subject's own visibility setting for this
|
|
436
|
+
app, resolved live at read time. Three rungs, per person per app:
|
|
437
|
+
- **not armed** — nobody but the writer. Arming is the master off-switch (see
|
|
438
|
+
`requestFollowersAccess()` below), and it is ON by default for a normal sharing app.
|
|
439
|
+
- **armed, visibility `"public"`** — everyone, signed-out visitors included. Both arms widen to
|
|
440
|
+
everyone at this rung.
|
|
441
|
+
- **armed, visibility `"followers"`** — the graph only: `followersOf` reaches the subject's
|
|
442
|
+
followers, `mutualsOf` only people they follow back. This is the default rung.
|
|
443
|
+
|
|
444
|
+
A subject's own setting can only SHRINK the reach, never grow it past what your arm allows — and a
|
|
445
|
+
privacy-sensitive app (a diary, a personal tracker) never reaches the public rung at all. So pick the arm
|
|
446
|
+
for the widest audience the content could ever legitimately want (`followersOf` for shared picks,
|
|
447
|
+
`mutualsOf` for both-directions-only), emit it the same way in every branch, and let the person's
|
|
448
|
+
own setting choose the rung. Your code never reads the rung to decide what to emit.
|
|
434
449
|
- Copy: "followers can see your picks" / "people you follow" — never "friends".
|
|
435
450
|
Following someone is low-stakes (it reveals none of YOUR data) — no confirm dialogs.
|
|
451
|
+
Next to a control that widens reach, say what it actually does: "Anyone can see this while your
|
|
452
|
+
profile is public"; for one that narrows it, "Going private keeps your existing followers" —
|
|
453
|
+
never "removes you from feeds". Turning sharing off, unfollowing, and blocking all mean the other
|
|
454
|
+
side **stops receiving** your new content; never write copy promising their existing copies are
|
|
455
|
+
erased or that they "can no longer see" what they already have.
|
|
436
456
|
- `useSocial()` also returns `followersEnabled: boolean` (read-only: can THIS app's
|
|
437
457
|
audience-gated docs currently reach the viewer's followers?) and
|
|
438
458
|
`requestFollowersAccess(): Promise<void>` (the preflight consent path). For a normal app,
|
|
@@ -440,7 +460,7 @@ docs in your database, and never build follow UI state machines.
|
|
|
440
460
|
audience UI on it or make users flip a switch first. It is `false` only for the minority:
|
|
441
461
|
the viewer explicitly turned this app off, chose a never-share standing preference, or the app
|
|
442
462
|
is marked privacy-sensitive (a diary, a personal tracker), where sharing stays off until a
|
|
443
|
-
deliberate opt-in. Show a small "open to followers so
|
|
463
|
+
deliberate opt-in. Show a small "open to followers so people can see your picks" callout
|
|
444
464
|
wired to `requestFollowersAccess()` ONLY when `!followersEnabled` (that minority), for an app
|
|
445
465
|
whose whole point is sharing; for a privacy-sensitive app this is the deliberate-consent
|
|
446
466
|
prompt. `requestFollowersAccess()` resolves quietly with no state change (including when the
|
|
@@ -448,14 +468,33 @@ docs in your database, and never build follow UI state machines.
|
|
|
448
468
|
on every render, or more than once per explicit user action. Don't invent a callout an app's
|
|
449
469
|
own privacy doesn't call for.
|
|
450
470
|
- For an explicit visibility control (a settings toggle inside your app), `useSocial()` returns
|
|
451
|
-
`visibility: "followers" | "private"` (read-only: the effective level for THIS
|
|
452
|
-
`followersEnabled` is just `visibility !== "private"`) and
|
|
453
|
-
`setVisibility(level): Promise<void>` to change it. `"
|
|
454
|
-
`"
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
471
|
+
`visibility: "public" | "followers" | "private"` (read-only: the effective level for THIS
|
|
472
|
+
viewer/app — `followersEnabled` is just `visibility !== "private"`) and
|
|
473
|
+
`setVisibility(level): Promise<void>` to change it. `"public"` means anyone can see, follower or
|
|
474
|
+
not, signed in or not; `"followers"` means the graph only (the default); `"private"` turns this
|
|
475
|
+
app's sharing off for the viewer. All three are settable — a three-rung control ("Anyone" /
|
|
476
|
+
"Followers" / "Only me") is the right shape for an app built around showing people your stuff.
|
|
477
|
+
The exception is a privacy-sensitive app (a diary, a personal tracker): a NEW `"public"` request
|
|
478
|
+
on one is REFUSED — the level stays exactly where it was, and like every other refusal
|
|
479
|
+
`setVisibility` resolves quietly with no error, so an "Anyone" rung there is a button that
|
|
480
|
+
silently does nothing. On a privacy-sensitive app, leave the "Anyone" rung out (or disable it)
|
|
481
|
+
and offer "Followers" / "Only me" only. (Read side, for completeness: a level stored `"public"`
|
|
482
|
+
before an app became sensitive is capped down to `"followers"` when anyone reads it.) Whichever
|
|
483
|
+
rungs you render, re-render from `visibility` after the await and show what actually took effect,
|
|
484
|
+
never what you asked for. Call `setVisibility` only from an explicit user action (a toggle).
|
|
485
|
+
Most apps don't need this — the default already shares with followers; reach for it only when
|
|
486
|
+
the app's own UX wants an in-app privacy switch.
|
|
487
|
+
- To show ANOTHER person's content on a profile page, hold an interest in them while their page is
|
|
488
|
+
open: `useInterest(handle)` from `use-vibes` (`useInterest(handle)` at the top of the page
|
|
489
|
+
component; a `null`/absent handle is a no-op, so call it unconditionally while the route param
|
|
490
|
+
resolves). Their content streams into this replica while you hold it and stops when the component
|
|
491
|
+
unmounts. It works for signed-out visitors too — a scanned QR code is often first contact — so
|
|
492
|
+
never gate it on being signed in, and never call it for the viewer's own content (you already
|
|
493
|
+
have that). Interest is DELIVERY only: the server hands over exactly what this viewer could
|
|
494
|
+
already read, so it never widens who may see anything, and asking about a private subject simply
|
|
495
|
+
delivers nothing. Dropping it is not revocation — docs already pulled stay put; what stops is
|
|
496
|
+
future delivery. Following is the durable upgrade: a follower's replica keeps pulling with no
|
|
497
|
+
interest held, so present follow as "keep seeing their picks", not as a permission request.
|
|
459
498
|
- To grow the graph, `useSocial()` returns
|
|
460
499
|
`invite(email: string): Promise<"ok" | "rate-limited" | "cancelled">` — asks the PLATFORM to
|
|
461
500
|
email a non-user an invitation to connect with the viewer. The platform shows a consent prompt,
|