softr-vibe-coding 2.14.0 → 2.14.1

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/CHANGELOG.md CHANGED
@@ -4,6 +4,10 @@ All notable changes to this skill are documented here. Versions follow [Semantic
4
4
 
5
5
  Entries from 1.3.1 onward are generated automatically from git commit subjects between version bumps (see `.github/workflows/publish.yml`). Entries before 1.3.1 were backfilled by hand from the existing commit history.
6
6
 
7
+ ## [2.14.1] - 2026-10-06
8
+ - Release 2.14.1
9
+ - Add nine more verified Softr facts from the 2026-09 production build
10
+
7
11
  ## [2.14.0] - 2026-10-06
8
12
  - Release 2.14.0
9
13
  - Add Softr runtime and Workflows facts verified on a 2026-09-18/19 production build
package/README.md CHANGED
@@ -205,7 +205,10 @@ softr-vibe-coding/
205
205
  │ │ # replace_node new ids, re-firing triggers,
206
206
  │ │ # write modes, serialExecution, continueOnError),
207
207
  │ │ # Softr DB row-gating recipe, what a push leaves
208
- │ │ # alone, DATETIME create shape, offset paging
208
+ │ │ # alone, DATETIME create shape, offset paging;
209
+ │ │ # OAuth grant per ticked workspace, email
210
+ │ │ # senders, formulas fixed at creation, loop
211
+ │ │ # counter, workflow time zone and publish state
209
212
  │ ├── browser-checks.md # Checking a pushed block in a browser with
210
213
  │ │ # the agent-browser CLI (ask before installing):
211
214
  │ │ # preview cookie, shadow-DOM refs grepped in the
@@ -293,7 +296,9 @@ softr-vibe-coding/
293
296
  │ # parsed as local dates, multi-value lookup shape
294
297
  │ # (Oct 6 2026)
295
298
  ├── rest-api.md # useProxyFetch + useQuery (full docs)
296
- ├── softr-database.md # Native DB — field IDs, no rate limits
299
+ ├── softr-database.md # Native DB — field IDs, no rate limits; checkbox,
300
+ │ # formula float, EMAIL lists, link label = display
301
+ │ # field, Zapier replaces multi-links (Oct 6 2026)
297
302
  ├── airtable.md # Column names, PAT vs OAuth, rate limits
298
303
  ├── google-sheets.md # Text formatting, 50-100 user cap
299
304
  ├── hubspot.md # 15 objects (listed ≠ usable), field model,
@@ -82,7 +82,7 @@ You'll see exactly which field is an object. Add `getFieldValue()` around it.
82
82
  | Date Range | `{ from: string, to: string }` |
83
83
  | Rating, Duration | `string or number or null` |
84
84
  | Select | `{ label: string, id: string }` |
85
- | Linked Record (via useRecord/useRecords) | `{ label: string, id: string }` — usually an array of these, but a link can arrive as a **single object** (verified live 2026-09-18); normalise with `Array.isArray(v) ? v : (v ? [v] : [])` |
85
+ | Linked Record (via useRecord/useRecords) | `{ label: string, id: string }` — usually an array of these, but a link can arrive as a **single object** (verified live 2026-09-18); normalise with `Array.isArray(v) ? v : (v ? [v] : [])`. `label` is the linked table's display field, so it changes if that field does (Softr Database, verified 2026-09-19; [softr-database.md](softr-database.md#gotchas)) |
86
86
  | Linked Record (via useLinkedRecords) | `{ id: string, title: string }` -- different! |
87
87
  | User, Created By, Updated By | `{ avatarUrl, id, name, email }` |
88
88
  | Attachment | `{ filename, id, type, url }` |
@@ -102,6 +102,11 @@ No API rate limits. Softr Database queries run internally without external API c
102
102
  - **Formula boolean values are strings.** A formula that evaluates to true returns `"1"`, not `true`. Always compare with `=== "1"` or `=== "0"`.
103
103
  - **Field IDs are opaque codes.** You cannot guess them from column names. Look them up via the ranked list above (MCP `database_list_fields` / bundled CLI / network inspector / Studio field drawer) — the generic Field Inspector block does NOT work for Softr Database.
104
104
  - **Relationships** work similarly to linked records in Airtable but use Softr's internal record IDs.
105
+ - **A checkbox reads back as a real boolean** (`true` / `false`), not a string like a formula's boolean above (verified live 2026-09-18).
106
+ - **Formula arithmetic is floating-point.** `1.15 * 400` rendered as `459.99999999999994` (seen 2026-09-18; the stored 1.15 was exact, the product was not). Wrap money and any other displayed product in `ROUND(…, 2)`.
107
+ - **An EMAIL field does not enforce one address.** A full read of a production table on 2026-09-01 found EMAIL-typed fields holding comma-separated lists. Split and trim before treating the value as one address. Passed whole into an email's To field, the addresses all see each other.
108
+ - **A link's `label` is the linked table's display field** (verified 2026-09-19). Change that table's display field in Studio and every label changes with it, so anything that matches on a label (block code, a Source condition, a workflow reference such as `[*].label`) silently stops matching. Match on the record id, or read the value from its own field.
109
+ - **Softr's Zapier "Update Record" action replaces a multi-link field's whole set** (confirmed by a Studio test, 2026-09-01). A zap that writes one record id into a link that allows several wipes the earlier links. Write the existing ids plus the new one.
105
110
 
106
111
  ## Best For
107
112
  - New projects starting from scratch
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "softr-vibe-coding",
3
- "version": "2.14.0",
3
+ "version": "2.14.1",
4
4
  "description": "Claude Code skill for generating production-ready Softr Vibe Coding blocks (JSX). Installs into ~/.claude/skills/ and auto-updates on each Claude Code session.",
5
5
  "bin": {
6
6
  "softr-vibe-coding": "bin/cli.js"
@@ -193,6 +193,8 @@ Permissions are chosen per workspace across **three areas with bundled levels**
193
193
 
194
194
  For block-building work you need **Applications & Forms: Full access** (to create/edit blocks) plus at least **Databases: View only** (schema discovery). Integrations browsing rides on Applications & Forms read access.
195
195
 
196
+ **Workspace membership is not enough.** The OAuth grant covers only the workspaces ticked on the authorization screen. On 2026-09-01 a user who was a member of a client's workspace in Softr still could not reach it through the MCP; re-authorizing the connector with that workspace ticked fixed it. When an app or workspace you can open in Studio is missing from `application_list` / `workspace_list`, check the grant under Settings → API tokens → Authorized apps.
197
+
196
198
  ## Vibe coding block tools
197
199
 
198
200
  Before writing any block code through the MCP, call `vibe_coding_block_get_docs` — it returns the current version of the [Vibe Coding Developer Guide](https://docs.softr.io/vibe-coding-developer-guide), which is the authority on hook signatures if it and this skill ever disagree. On runtime *behaviour* the guide's prose can lag a live capture, and where it does this skill says so: the guide still describes `useRecords({ enabled })` as a way to defer loading (checked 2026-10-06), while a 2026-09-18 network capture showed `useRecords` fetching anyway ([reading.md](../datasources/reading.md#userecords-ignores-enabled-false)). Trust the capture until a newer one says otherwise.
@@ -547,7 +549,7 @@ verified on HubSpot on 2026-10-05, the same way.* There are two forms, and **the
547
549
  pick it in a block's Source tab, save, and read `dataSources[].condition` back with
548
550
  `vibe_coding_block_get_settings`. That is how the user-field form was found.
549
551
  - **CONTAINS against a list of emails has a substring trap:** `bob@x.com` matches a field holding
550
- `jbob@x.com`. Prefer IS against a single-email field. If a record must hold several emails, the
552
+ `jbob@x.com`. Prefer IS against a single-email field, and remember that an EMAIL-typed field can still hold a list ([../datasources/softr-database.md](../datasources/softr-database.md#gotchas)). If a record must hold several emails, the
551
553
  delimiter trick the embedded form was meant to provide does not work, so accept the trap or
552
554
  split the data.
553
555
  - When a row gate must follow something other than the user's email (a company, a team), compare
@@ -614,6 +616,8 @@ The Applications area goes well beyond reads (roster as delivered 2026-10-01; be
614
616
  | Publish / preview | `application_preview`, `application_publish` |
615
617
  | Workspace | `workspace_list`, `workspace_list_email_senders`, `get_workspace_integrations` (distinct from the [integrations drill-down](#browsing-integrations-external-data-sources) below) |
616
618
 
619
+ **Email senders, as the MCP shows them** (read 2026-09-10 and 2026-09-18): `workspace_list_email_senders` lists each workspace sender with a `confirmed` flag, and an address that was added but never verified reads `confirmed: false`. `application_get` showed the app's own sender as `<subdomain>@softr.app`. In a workflow, a `SOFTR_SEND_EMAIL` node chooses its sender through the optional `emailSenderSignatureId` input. Before publishing a workflow that must send from a particular address, check that the address is in the list and confirmed.
620
+
617
621
  Combined with the database tools (`database_create` / `database_create_table` / `database_create_field`) and `vibe_coding_block_create` + `application_publish`, the tool set for scaffolding a full app end to end now exists. (Existence-verified only — that pipeline hasn't been run live; treat the first full scaffold as an experiment, not a routine.)
618
622
 
619
623
  **Etiquette from the server's own instructions:** after changing a block, link the page as `https://studio.softr.io/applications/{applicationId}/pages/{pageId}`; offer `application_preview` or `application_publish`, but **only publish when the user asks**.
@@ -767,6 +771,10 @@ Known limits and behaviors (per official docs):
767
771
  - **`database_create_field` for a DATETIME takes `options: {"includeTime": true}`** (ours, verified
768
772
  2026-09-18). The options shape `database_list_fields` returns for an existing DATETIME field is
769
773
  rejected, so do not copy a field definition from a read into a create.
774
+ - **A formula cannot be changed after creation.** `database_update_field` with a new `formula` is
775
+ refused: `BAD_REQUEST` "[formula] can only be set when the field is created; delete the field and
776
+ create it again to change it" (seen 2026-09-18, under the old name `update_field`). Edit the formula
777
+ in Studio instead.
770
778
  - **SINGLE_LINE_TEXT fields carry a 1,024-character `maxLength`** (ours: two fields of a production
771
779
  table, found in a 2026-09-10 schema audit and confirmed live 2026-09-18, recorded in a block's code
772
780
  comment). A block that appends to such a field has to keep the total under it; what a longer write
@@ -820,6 +828,7 @@ those; see [the rename note](#tool-names--the-2026-10-01-rename).
820
828
  - **Over a plain array of strings** (a `CUSTOM_CODE` node's `$.body.<key>`, say), the item *is* the value: reference it as bare `{loopActionGroup.<id>:::loopVariables.items}`, nothing after `items` (2026-09-19; accepted by the validator, not yet exercised by a run).
821
829
  - **References into the loop are rejected until the source node's saved sample holds at least one item** (the validator says so). Test the source node on a record that yields a non-empty array before wiring the steps inside the loop.
822
830
  - **To put a step inside the loop**, call `workflow_add_node` with `compositeNodeId: <loopNodeId>`. The step lands in the loop's own `actions` / `paths`, not the workflow's.
831
+ - **The loop has a `loopCounter` input**, `{ start, end, step, maxIterations }`. `maxIterations` can be set over MCP and reads back (2026-09-19). We have not run a loop long enough to reach the cap.
823
832
  - **An empty loop does not stop the run** (recorded 2026-09-19). Zero items means zero iterations and a normal completion, and the steps after the loop still run. A guard stamp placed after a loop therefore fires even when nobody was emailed, and consumes the notice. A gate on the item count has to cover the loop and the stamp together; gating only the stamp leaves the guard unset, and the next edit sends again.
824
833
  - **`workflow_update_node_inputs` batches validate against the STORED node state**, not the batch-in-progress — an update that depends on another update in the same batch fails validation. Split dependent updates into sequential calls.
825
834
  - **`CUSTOM_CODE` contract** (2026-09-18/19; found by testing, documented nowhere we know of):
@@ -844,6 +853,8 @@ those; see [the rename note](#tool-names--the-2026-10-01-rename).
844
853
  - **`serialExecution: true`** in a workflow's configuration (set with `workflow_update_configuration`, confirmed by read-back, 2026-09-18) makes runs queue instead of overlapping. We set it on a workflow that appends to a multi-link field: two runs at the same moment would each read-modify-write the same array, and one append could be lost.
845
854
  - **`continueOnError` is stored on the path, not on the node** (2026-09-19). Turned on with `workflow_update_node_continue_on_error`, it shows up as a `SUCCEEDED OR FAILED` condition on the node's outgoing `paths` entry. Without it a failed step ends the run, so a guard stamp after it never happens and the whole notice goes out again on the next edit. On the **last step inside a loop** the entry has the condition but **no `toActionId`**, and whether the engine reads that as "carry on with the next item" is unproven. Test it with one deliberately bad item mid-list and check that the items after it still ran.
846
855
  - **Time-based sends** (a proof of concept, 2026-09-01): a formula field flips (to `"yes"`, say) on the target day, a filtered view picks up the records where it has flipped, and a "Record enters view" trigger fires when one enters.
856
+ - **Each workflow has its own `configuration.timeZone`.** In one workspace every MCP-built workflow read `UTC` and an older one `Europe/Athens` (2026-09-19). Read it with `workflow_get` before relying on dates, times or a time-of-day window inside a workflow.
857
+ - **Read publication state from `workflow_get`.** A workflow that was never published reads `enabled: false` and `enabledVersion: null` (2026-09-01); the one running workflow in the same workspace read `enabledVersion: 0` (2026-09-19).
847
858
  - **Test-safety rules** (which `testRunMode` means what in practice):
848
859
  - Record-**write** nodes (`SOFTR_TABLES_UPDATE_RECORD` etc.) are `REAL_ONLY` — **never test them against a production workspace**; the test performs the real write.
849
860
  - `SOFTR_SEND_EMAIL` is `MOCK_AND_REAL` — **always pass `mode: "mock"`**.