@foldspace_npm/harness 0.1.33 → 0.1.34

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.
@@ -2,9 +2,23 @@ export const AGENT_API_NAME = {{AGENT_API_NAME_JSON}};
2
2
  export const PRODUCT_ID = {{PRODUCT_ID_JSON}};
3
3
  export const APP_DOMAIN = {{APP_DOMAIN_JSON}};
4
4
 
5
+ /**
6
+ * Whether each of the product's customers has their own subdomain
7
+ * (acme.example.com, globex.example.com). Left undecided until the human has
8
+ * answered: `true` or `false` is their answer, never a guess.
9
+ */
10
+ export const PER_CUSTOMER_SUBDOMAIN: boolean | undefined = undefined;
11
+
5
12
  /**
6
13
  * Empty until you capture the app's own XHR. A guessed host looks like it
7
- * worked. Include an explicit port if the app uses one.
14
+ * worked. One of:
15
+ * - `"https://api.example.com"` - every customer calls the same backend.
16
+ * Include an explicit port if the app uses one.
17
+ * - `"/api"` - the backend is on the page's own host.
18
+ * - `"https://{subdomain}.api.example.com"` - each customer's backend carries
19
+ * their subdomain; the runtime puts each user's own in.
20
+ * With `PER_CUSTOMER_SUBDOMAIN = true`, never the address you observed as it
21
+ * is: that is one customer's backend, and every user's agent would call it.
8
22
  */
9
23
  export const API_BASE = "";
10
24
 
@@ -14,6 +14,9 @@
14
14
  * - `apiFetch` - JSON + `Authorization: Bearer`. For any other sign-in, see
15
15
  * "Custom auth" below.
16
16
  * - `apiFetchBinary` — same auth, raw bytes (file download).
17
+ * - `apiBaseUrl` — `API_BASE` for this page: `"/api"` on the page's own host,
18
+ * `{subdomain}` filled with this user's own. A custom `apiFetch` builds its
19
+ * URLs from it, never from `API_BASE` directly.
17
20
  * - `publicFetch` — no token; marketing / public origin only.
18
21
  * - `getAuthToken` / `parseJwt` / `redact` — session read and safe logging.
19
22
  * - `mapWithConcurrency` — cap parallel fetches over a list.
@@ -1,5 +1,5 @@
1
1
  {
2
- "$comment": "Written by foldspace init: the commands a build runs, allowed up front so the coding agent is not stopped one denial at a time. init and upgrade are left out on purpose, so they ask, like anything not listed. foldspace deploy is Foldspace-internal and never runs in a build.",
2
+ "$comment": "Written by foldspace init: the commands a build runs, allowed up front so the coding agent is not stopped one denial at a time. init and upgrade are left out on purpose, so they ask, like anything not listed; so is share upload, which sends a video out. foldspace deploy is Foldspace-internal and never runs in a build.",
3
3
  "permissions": {
4
4
  "allow": [
5
5
  "Bash(npx foldspace help:*)",
@@ -12,6 +12,8 @@
12
12
  "Bash(npx foldspace badge:*)",
13
13
  "Bash(npx foldspace published:*)",
14
14
  "Bash(npx foldspace record:*)",
15
+ "Bash(npx foldspace share inspect:*)",
16
+ "Bash(npx foldspace sync:*)",
15
17
  "Bash(npm install:*)",
16
18
  "Bash(npm run build:*)",
17
19
  "Bash(npm run typecheck:*)",
@@ -0,0 +1,135 @@
1
+ ---
2
+ name: review-experience
3
+ description: Check an experience's code before every save of the agent's code - it returns only the data the answer needs, every write waits for the user's yes, and it reaches nothing the user cannot reach in the product. A serious finding stops the save. Use before each repo_write of agent code, after `npx foldspace build` passed - not for a save that only brings agent/runtime/ up to date.
4
+ ---
5
+
6
+ # Review an experience before it is saved
7
+
8
+ Saving puts the code where it can be published for every user of the
9
+ product - each with their own data, their own role and their own sign-in.
10
+ What was right for the builder's account in the test window has to be
11
+ right for all of them. This review is the last look before that.
12
+
13
+ `npx foldspace build` already checked what can be read off the code: one
14
+ customer's address written in, the user's token leaving the page,
15
+ secrets, calls to hosts the project does not name. Those are not repeated
16
+ here, and cards may draw any markup they need. This review is the three things only
17
+ someone who knows what the experience is for can judge.
18
+
19
+ ## When
20
+
21
+ Before **every** save of agent code with `repo_write` - the first one and
22
+ each one after. Run it on the experiences the save changes; a save of only
23
+ the files a build put in `agent/runtime/` needs no review.
24
+
25
+ Build first: a save waits for `npx foldspace build` with no errors, then
26
+ this review.
27
+
28
+ ## 1. Least data
29
+
30
+ What `execute` returns reaches the agent and the model, and for a view card
31
+ it is what the agent answers from (rule 2 of the manual). Return the facts
32
+ the answer needs, picked one field at a time - nothing else.
33
+
34
+ Read each `execute` and each card `callback` and check:
35
+
36
+ - **Fields are picked by name.** Never `res.data`, a whole record, or
37
+ `...record` from the API.
38
+ *"Which invoices are due?"* needs invoice number, amount, due date and
39
+ status - not the customer record they came with (address, phone, tax id,
40
+ notes, the account manager).
41
+ - **Only the ids the card or the next action uses.** An internal id nothing
42
+ reads is data for nothing.
43
+ - **Other people's details only when the question is about them.** A list of
44
+ deals needs the deal owner's name, not their email and phone.
45
+ - **Rows the card shows, not the whole table.** Cap the list at what the card
46
+ draws (and say how many there are in all, if the answer needs it).
47
+
48
+ **Serious:** a whole record or the raw response returned; personal details
49
+ (email, phone, address, date of birth, payment details, government ids) the
50
+ answer does not need. **Fix now, not serious:** an extra harmless field.
51
+
52
+ ## 2. A yes before every write
53
+
54
+ The agent acts with the user's own sign-in: a write is the user writing.
55
+ It happens only after the user said yes to exactly that change.
56
+
57
+ Find every write in the experience: an `apiFetch` (or `fetch`) with
58
+ `method` `POST`, `PUT`, `PATCH` or `DELETE`, a form post (`submitForm`), and
59
+ any helper in `agent/api/` that calls one. For each:
60
+
61
+ Every write gets the user's yes in one of two ways:
62
+
63
+ - **A card (visual widget), on the user's press.** The card shows what will
64
+ change before they press - `recipes/create-a-record` has the shape.
65
+ *Cancel the Pro plan* is a card that says *"Cancel your Pro plan on 1
66
+ November? You keep it until then."* with **Cancel plan** and **Keep it**;
67
+ nothing is sent until **Cancel plan** is pressed, and **Keep it** or closing
68
+ the card writes nothing.
69
+ - **Text only (no card): the action is marked confirmation required.** Set
70
+ `confirmation_required: true` and a short `confirmation_message` on the
71
+ action (the Foldspace action tools take both; in Agent Studio it is the
72
+ action's *confirmation required* setting). Foldspace then asks the user
73
+ before `execute` runs.
74
+
75
+ - **It writes what the user saw** - the same record, the same values - and
76
+ nothing besides: no "mark as read", no second record on the side.
77
+ - **It is never repeated by itself.** A write that timed out may have landed:
78
+ say so, as `recipes/update-meeting-notes` does, rather than sending it again.
79
+
80
+ ⚠️ One case may need neither - this is a reading of
81
+ `recipes/update-meeting-notes`, not yet a confirmed rule: **adding one new
82
+ thing whose words the user gave in the same message** (*"add this to the
83
+ meeting notes: ..."*). Their message is the yes, as long as `execute` writes
84
+ exactly what they said and returns what was written. When in doubt, a card or confirmation required.
85
+
86
+ **Serious:** a write with neither a card nor confirmation required that removes, cancels, replaces or
87
+ overwrites something; sends something to someone (an email, an invite, a
88
+ message); spends or refunds money or changes a plan; changes who can see or
89
+ do what; or touches more than one record. A write the user did not ask for.
90
+
91
+ ## 3. Nothing the user cannot reach in the product
92
+
93
+ The experience may reach only what this user could reach in the product
94
+ itself. The builder is often signed in as an owner or an admin; most users
95
+ are not.
96
+
97
+ - **Every call is one the product's own screens make**, seen with
98
+ `npx foldspace observe screen` on a screen `observe menu` lists - not a path
99
+ found in the app's code, in its API docs, guessed, or reached by typing an
100
+ address. `docs/app-profile.md` says how each endpoint was established.
101
+ - **A screen only an admin sees stays an admin's.** When the builder's
102
+ account can see admin screens (Users, Billing, Settings for the whole
103
+ account, anything under `/admin`), ask the human once, as a choice:
104
+ *"Do all your users see <the screen>, or only admins?"* Only admins: this
105
+ experience is not for everyone - say so, and do not save it as one.
106
+ - **Nothing widens the reach.** No `all=true`, `scope=account`,
107
+ `includeDeleted`, export-everything or impersonation calls. A user's,
108
+ team's or account's id comes from the session (`/me`,
109
+ `recipes/who-is-the-user`) or from an earlier action's result - never from
110
+ the user's or the model's words.
111
+ - **Do not rely on the screen being hidden.** Some products hide a screen and
112
+ still answer its call for anyone signed in. The test is what the product
113
+ shows this kind of user, not what its backend lets through.
114
+
115
+ **Serious:** a call to an endpoint no screen for this kind of user makes; an
116
+ admin-only screen in an experience for everyone; an id that widens the reach
117
+ taken from words.
118
+
119
+ ## 4. What happens next
120
+
121
+ - **Nothing serious:** save. In chat, one line at most - *"Checked it before
122
+ saving."* Fix the not-serious ones in the same pass, before the save.
123
+ - **Something serious:** do not save. It is your code: fix it - pick the
124
+ fields, move the write behind a card, drop the call - build again, and run
125
+ this review again. Then save.
126
+ - **A serious finding only the human can settle** (whether regular users see
127
+ a screen, whether a write needs a card): ask as a choice, with the safe
128
+ option first, and save only after the answer settles it. Their answer goes
129
+ in `docs/app-profile.md`, with the date.
130
+ - **Never save around it** - not "for now", not to dev only: dev is where their
131
+ users can be sent next.
132
+
133
+ Nothing here is said to the human in these words: in chat it is *"it only
134
+ shows the invoice details, not the whole customer record"*, *"cancelling
135
+ asks the user first"* - what their users will see.
@@ -182,7 +182,9 @@ If `ok` is false, find its `code` in the table below. If true, read the poster
182
182
  ended: each answer complete, none an error or "that action isn't available",
183
183
  and each one shows what its capabilities and line say. A bad one: offer
184
184
  another question for that use case, prove it, record again. If there is a
185
- `note` that it ran long, offer to drop the last scene.
185
+ `note` that it ran long, offer to drop the last scene. If there is a
186
+ `wideWindow` note, tell the human in one line and offer a narrower test window
187
+ and a new recording; they decide.
186
188
 
187
189
  ## 6. Hand it over
188
190
 
@@ -230,7 +232,12 @@ Only on a yes:
230
232
  5. Tell them in one line that they get an email at the first view and a daily
231
233
  summary on days it is watched, and that you can show the numbers
232
234
  (`get_video_share_stats`) or delete the link (`delete_video_share`) whenever
233
- they ask.
235
+ they ask. `publish_video_share` says whether those emails are on for this
236
+ share.
237
+ 6. They want the emails off, or back on: the tool
238
+ `set_video_share_notifications` with the `share_id` and
239
+ `email_notifications` (`false` stops them, `true` turns them on). It covers
240
+ that one video only.
234
241
 
235
242
  The tools refuse with a reason you pass on as it is: an EU account (share
236
243
  links are not offered there yet - send the file instead), a cap (shares per