---
name: bluestep-reference
description: On-demand BlueStep (B6P) platform reference — BsJs/RelateScript patterns, conventions, and gotchas. Consult when implementing, reviewing, or documenting a BlueStep component. This file is the manifest of single-topic instruction files (reference/, conventions/, gotchas/) with a one-line "load when" trigger for each; read it first to pick the right file.
---

# BlueStep Instruction Index

This is the navigation hub for the on-demand BlueStep rules. It is read when needed, not auto-loaded — Tier-1 critical rules stay in `AGENTS.md`, and the Tier-2 overviews (`b6p-platform.md`, `bsjs-development.md`) cover the high-frequency platform and BsJs material. Consult an entry below when a task needs deeper, single-topic detail; each file is self-contained and is reached in one hop from here.

Entries read: `path — what it covers. Load when <trigger>.` Match the trigger keywords to the task at hand, then open that one file.

## reference/ — single-topic platform and API detail

- [reference/ai-services.md](reference/ai-services.md) — `B.ai`, tenant-metered model access: synchronous `call()`/`agent()`, typed tools that fill Relate form entries, spend budgets by flag, and AI write provenance via `RowContext.ai()`. Load when a component needs model access, AI generation, agents, or AI tools.
- [reference/api-patterns.md](reference/api-patterns.md) — B API usage: Java optionals (`.opt()/.orElse()`), field types and access, `mergeTag()` L/F/I/H codes, multi-entry-form (MEF) entries, query access, Java collections, writing date/datetime fields, `B.commit()` and formula triggers, endpoint request/response, base64, string escaping/HTML-stripping (`B.text`: `escapeHtml`, `toPlainText` — use before hand-rolling escaping or regex), user/session. Load when calling the `B` API, reading or writing fields, escaping output, or wiring an endpoint's request/response.
- [reference/b-include-element.md](reference/b-include-element.md) — `<b-include>` browser custom element that fetches HTML from a URL and renders it inline (client-side `<jsp:include>`/SSI); `src`/`run-scripts`/`csrf` attrs, spinner/error behavior, trusted-template-only (neutralized in user HTML). Load when injecting a dynamic async HTML fragment in the browser.
- [reference/chronounit-months.md](reference/chronounit-months.md) — `ChronoUnit.MONTHS.between` counts elapsed (day-aware) months, not calendar-month boundaries. Load when computing a month difference or porting SQL `DATEDIFF(MONTH)`.
- [reference/code-patterns.md](reference/code-patterns.md) — working BsJs skeletons: queries, MergeReport server/client bridge, endpoints, error handling, performance (per-row queue, lazy init), component import, debugging. Load when starting a query, MergeReport, or endpoint and you want a known-good pattern.
- [reference/component-library.md](reference/component-library.md) — BlueStep generic components (`moduleF`, `headF`, `svgF`, `tableF`), Bootstrap 3.3.7 fallback, `config.json` registration with short import, CSS anti-pattern, and when not to use components. Load when building MergeReport UI or generating HTML.
- [reference/crm-dashboard-inspo.md](reference/crm-dashboard-inspo.md) — multi-page dashboard SPA pattern: a thin server flattens form data into one JSON payload island, a single client bundle renders a tabbed SPA. Load when building a data-dense, multi-page MergeReport dashboard.
- [reference/csv-parsing.md](reference/csv-parsing.md) — MergeReports can read and parse uploaded CSVs via the built-in `B.csv()` parser plus DocumentLinkField/Document/fetch. Load when parsing an uploaded CSV in a MergeReport.
- [reference/dashboard-design-system.md](reference/dashboard-design-system.md) — the house design language (palette, type, cards, tabs) for dashboard MergeReports; do not use Material/rounded-tonal styling. Load when building any new dashboard UI.
- [reference/datetime-field-write.md](reference/datetime-field-write.md) — set a DateTimeField with `field.val(zonedDateTime)`; `.dateTimeVal()` has Graal-ambiguous overloads. Load when writing a DateTimeField.
- [reference/design-system.md](reference/design-system.md) — overview of the BlueStep Design System: component source priority, design standards, color palette, and the link to the interactive reference. Load when you need design standards or the canonical design-system URL.
- [reference/dpn-dashboard-framework.md](reference/dpn-dashboard-framework.md) — parameterized data-endpoint + dashboard framework: one endpoint serves per-entity/date-range JSON via URL params, a thin-server dashboard fans out throttled parallel fetches. Load when building a client-fed analytics dashboard.
- [reference/endpoint-method-call.md](reference/endpoint-method-call.md) — `B.net.request.method` is a method (call it); without parens it type-checks but returns the Function. Load when reading the HTTP method inside an endpoint.
- [reference/endpoint-no-delete-method.md](reference/endpoint-no-delete-method.md) — endpoints don't route DELETE (or other non-GET/POST/PUT methods); use a PUT/POST action discriminator. Load when designing endpoint routing or a delete action.
- [reference/endpoint-output-channel.md](reference/endpoint-output-channel.md) — `/b/` endpoints write the body via `B.net.response.out()`, not `B.out` (the MergeReport channel, which yields a blank Error page). Load when an endpoint returns a blank or Error page.
- [reference/endpoint-urls.md](reference/endpoint-urls.md) — endpoints are reached at `/b/<alias>`, not the `/files/{id}/draft/` path; the alias is per-endpoint. Load when testing an endpoint or giving a user its URL.
- [reference/entry-delete.md](reference/entry-delete.md) — delete a multi-entry-form entry with `entry.entry().delete()`, not `entry.delete()` (the outer typed declaration lies). Load when deleting an MEF entry.
- [reference/file-execution.md](reference/file-execution.md) — which files execute and when: `scripts/app.ts` (server) vs `static/index.html` (client), Workspace Sync, common mistakes. Load when a file isn't running or you're unsure of the server/client split.
- [reference/http-requester.md](reference/http-requester.md) — method names on `B.net.httpRequester` (`.method()`, `.responseCode()`) use overloaded getter/setter style, not Java-bean `.setMethod`/`.getResponseCode`. Load when making a non-GET external HTTP call.
- [reference/id-full-vs-short.md](reference/id-full-vs-short.md) — `Id.toString()` is the full `ClassID___ShortID`; `shortId()` is just the trailing number; `optById` needs the full form. Load when round-tripping an entry id or calling `optById`.
- [reference/import-scope.md](reference/import-scope.md) — form/field imports are scoped: current-record (only with a primary form attached) vs. specific named queries; a field must be imported on **every** query whose record type reaches the code — the runtime-read query AND any query used as a declared parameter type — and the scope is verifiable against `declarations/`. Load when planning or wiring a new form/field import — deciding current-record vs. which query/queries to import on.
- [reference/internal-loopback-fetch.md](reference/internal-loopback-fetch.md) — endpoint-to-endpoint loopback within an org uses `B.net.fetch` on a relative path with `credentials:true`, not the external `httpRequester`. Load when one endpoint calls another in the same org.
- [reference/localdate-parse.md](reference/localdate-parse.md) — `Java.Time.LocalDate` is a TypeScript namespace only; use `B.time.LocalDate` for runtime `parse()`/`now()`. Load when parsing or creating a LocalDate.
- [reference/merge-report-async-loading.md](reference/merge-report-async-loading.md) — the "Asynchronous Loading" option on a Data Merge Report lazy-loads it AFTER the page resolves (BSJS used an `async` metadata property, not the checkbox); parent→child fan-out, the obsolete `formFooter` hack, and the script-timing gotcha. Load when a merge report is slow or should lazy-load, or page script depends on async merge content.
- [reference/merge-report-memo-json.md](reference/merge-report-memo-json.md) — the "MergeReport + memo field JSON" framework: embed a custom interactive widget on a form that persists its state as JSON in a hidden memo field. Load when building a stateful custom widget on a form.
- [reference/merge-report-static-index.md](reference/merge-report-static-index.md) — with a `static/` bundle, `B.out` (server content) and `static/index.html` (client markup) both render but are completely disjoint (B.out is injected as a tag that runs *after* index.html), and `static/styles.css` + `.build/script.js` load automatically; put the mount + config island in `index.html`, keep CSS in `styles.css` (never fetch/inline it), use `B.out` only for final server-rendered markup, and get client data from the endpoint — not a `B.out` config island. Load when a MergeReport has a `static/` bundle.
- [reference/merge-report-urls.md](reference/merge-report-urls.md) — BSJS equivalents of relatescript `lookupMergeReport`/`lookupMergeReportScript`, how to get `viewUrl`/`printUrl`, and the **record-scoped `_id`/`_id2` URL** for opening a report on a specific record (`viewUrl()` alone is record-less → "No Record Selected"). Load when looking up a MergeReport URL in code, or linking a user to a report on a specific record.
- [reference/multi-entry-in-multi-entry.md](reference/multi-entry-in-multi-entry.md) — the "multi entry in a multi entry" pattern: many notes/updates against one MEF entry, stored as JSON in one memo field. Load when users need repeated sub-entries under a single form entry.
- [reference/named-controls-submit.md](reference/named-controls-submit.md) — any HTML control with a `name` attribute inside a MergeReport form gets submitted and breaks the save. Load when adding form controls to a MergeReport settings UI.
- [reference/new-entry-id.md](reference/new-entry-id.md) — pre-commit `shortId()` returns `"0"` (`id.isValid()` is false); commit before reading a new entry's id. Load when reading the id of a freshly-created entry.
- [reference/relationship-field-set.md](reference/relationship-field-set.md) — `RelationshipField.set()` needs the target form-entry id, not a Record id, or the formula silently aborts; watch for `as any` casts hiding the bug. Load when setting a RelationshipField.
- [reference/send-message-abort.md](reference/send-message-abort.md) — `B.net.sendMessage(msg, abort)` aborts saves, but in pre-delete formulas the message does not surface in the UI. Load when aborting a save or delete from a formula.
- [reference/session-cookie-forwarding.md](reference/session-cookie-forwarding.md) — `request.cookies()` filters HttpOnly cookies; use `optHeader("Cookie")` to grab `JSESSIONID` for authenticated loopback calls. Load when forwarding the session cookie to a loopback call.
- [reference/singleselect-null-copy.md](reference/singleselect-null-copy.md) — copying an empty SingleSelectField via `val(other.val())` throws `set(null.id())`; guard with `opt().isPresent()`/`clear()`. Load when copying a SingleSelectField value.
- [reference/staff-query-permission-gating.md](reference/staff-query-permission-gating.md) — permission-gating concept: match the current user to a staff record via a query, read a per-staff SingleSelect permission, emit capability flags (fail-closed, super-user override); plus the authoring side — a permission/security query carries **exactly one** display column, the staff `Full Name` field, set at CREATE and resolved by name (topIds are per-org). Load when gating a MergeReport UI by per-user permission, or when creating a permission/security query.
- [reference/timefield-vs-datetimefield.md](reference/timefield-vs-datetimefield.md) — `zonedDateTimeParts` works on a DateTimeField (ZonedDateTime); for a TimeField (LocalTime) use a separate `HH:mm` extractor. Load when extracting parts from a TimeField vs a DateTimeField.
- [reference/user-zone-id.md](reference/user-zone-id.md) — `B.time.userZoneId()` returns the user/system ZoneId; `B.userZoneId()` does not type-check. Load when getting the user or system time zone.
- [reference/version-stream-previous-value.md](reference/version-stream-previous-value.md) — read the previous committed version with `entry.versionStream().findFirst().orElse(null)` and diff current-vs-previous field values (normalize-and-compare helper, first-create null guard, pre-save vs post-save timing caveat) — no stored "old value" field needed. Load when detecting whether a field changed between saves / needing the previous value of a field.
- [reference/vite-spa-merge-report.md](reference/vite-spa-merge-report.md) — off-platform Vite/Preact SPA merge report: build a full SPA off-platform (Node + npm) and deploy the minified `dist/` into the report's `static/` via deploy-lib (the CLI's static build is bypassed); when to use vs the CLI-compiled `static/script.ts` path, architecture (report serves `index.html`, `app.ts` is a no-op or a `B.out` window-bootstrap), the two data models (endpoint fetch carrying the session vs. server bootstrap needing `objects/imports.ts`), history in GitHub. Load when building or reviewing a merge report as a Vite/Preact (or React) single-page app, or deciding between the off-platform bundle and the CLI-compiled path.
- [reference/mcp-read-multi-entry-forms.md](reference/mcp-read-multi-entry-forms.md) — reading multi-entry form entries in-session: `form_entry` READ is broken for multi-entry (resolves to `NEW_MULTI`), `formRows` ignores `limit`/`offset`, returns XMLEncoder blobs and times out on huge forms, `fieldData` is one-field-one-entry (batch via GraphQL aliases) — **prefer `relateQuery` over a stored `List` query**, the only paged, filterable, value-bearing path (create one if missing). Plus the `listId` arg-name gotcha. Load when reading multi-entry form data over MCP.
- [reference/fid-alternate-identifier.md](reference/fid-alternate-identifier.md) — how a component acquires its code-facing name: the `FID=<name>` alternate identifier (formulas/reports/queries), the field's dedicated Formula ID property, and the ON_DEMAND Identifier — three different mechanisms, and which one resolves what. Load when a formula/report/query/field needs to be addressable by a stable name in code, or you see `__FID_<name>` in a full id.
- [reference/merge-report-entry-id-patch.md](reference/merge-report-entry-id-patch.md) — an inline-embedded BSJS MergeReport with a `static/` bundle can't read `entryId` from the page URL; `app.ts` sets `data-entry-id` on the mount via a `B.out` script (injected after `index.html`, before boot). Load when an embedded report renders "missing data-entry-id".
- [reference/git-site-spa.md](reference/git-site-spa.md) — a **Git site** serves a GitHub repo under `/spa/` on its domain; two purposes with different Vite bases: a **mounted bundle** loaded into another page, or a **standalone routed SPA** at the domain root; data goes over the same-origin `/gql` GraphQL surface (`/b/` endpoints only for the four exceptions). Load when building or serving a SPA from a GitHub repo, or wiring its data layer.
- [reference/gql-in-a-tab.md](reference/gql-in-a-tab.md) — how to run any GraphQL query or mutation on an org: the six tools release 26.10.21.02 took off the gateway and which gateway tool covers each read instead, probe with `describe_tool`, use the gateway's `graphql_query` / `graphql_mutation` where the org serves them, otherwise POST to `/gql` from a browser tab signed in to that org (Claude opens the sign-in page, CSRF token from a `HEAD`, `errors` before status, re-check who is signed in before a write, few prompts per write); with no route, tell the user in plain words what was not done. Load before any GraphQL read or write, or when the gateway answers `TOOL_NOT_FOUND` for `graphql_query`, `get_record` or `query_records`.
- [reference/option-list-export-values.md](reference/option-list-export-values.md) — `get_option_list` omits export values; read them via GraphQL `RelateListItem.value` — inline fragments required on `items`, a GROUPED list nests the real items one level down (`[{},{}]` = groups, not empty items), and `sortOrder` is global across groups. Load when reading option-list export values, or when `get_option_list`'s fields aren't enough.
- [reference/relatescript-relationship-fields.md](reference/relatescript-relationship-fields.md) — in RelateScript, indexing an empty relationship field is safe: `relField.selected[0].id` / `.name` resolve `null` rather than throwing, and `if (relField.selected[0].id == null)` is the idiomatic emptiness check; distinct from the BsJs `.set()` hazard. Load when reading or reviewing a relationship field in RelateScript, or before flagging an unguarded `selected[0]` read.
- [reference/relate-consolidated-entry-reports.md](reference/relate-consolidated-entry-reports.md) — a one-row-per-entry listing across a multi-entry form is a Relate Report (not a MEFR, which has no standalone run surface, and not a record-level query): the 5-step wizard, and the browser-driving mechanics for an option-list search criterion (`updateValueShow`, `add3()` is a server submit). Load when building a cross-record entry listing or export in Relate.
- [reference/current-record-bindings.md](reference/current-record-bindings.md) — the two current-record bindings of a multi-entry form: `[all entries]` (the built-in unnamed EntityList MEFR `list_views` shows with a null displayName — a collection) vs `[current entry]` (a single entry, offered to any non-scheduled component whose primary form is multi-entry), and the three cases that justify a custom MEFR. Load when wiring a multi-entry form into a script, formula, or merge report, or before creating a MEFR.
- [reference/multi-entry-form-sorting.md](reference/multi-entry-form-sorting.md) — the `[all entries]` view sorts on the first summary field only (a MEFR is not a fix until its sort is configured); safe sort-key designs when code reads the latest entry (a null sorts above real data); Date vs DateTime; never group a report on a DateTime or Signature field, add a hidden plain-date companion instead. Load when designing a multi-entry form or reading its latest entry.
- [reference/client-side-form-api.md](reference/client-side-form-api.md) — the client-side form API for JS embedded in Relate merge and form pages: `_SR_.onLoad(fn)` is the init hook (not DOM-ready), `getElementObj(fieldName)`, `.setRequired(bool)`, `showHide(selector)`; `setRequired(true)` silently does nothing on a field inside a collapsed section. Load when writing or reviewing client JS that reads form elements, toggles sections, or makes a field required at runtime.

## conventions/ — build, deploy, and workflow directives

- [conventions/client-data.md](conventions/client-data.md) — no real client or contact data in anything committed: placeholders (`<client A>`, Jane Doe, 555-01xx, `name@example.com`), record ids are fine, real data only in git-ignored `.claude/scratch/`; the write-time hook, `pre-commit` and opt-in `pre-push` model review (`git config bluestep.phiReview true|false`), what to do when one blocks (`phi-ok` for test data only, `.phi-allow` format, `--no-verify` last), `phi-check.sh --all`, and cleaning up a leak that already landed. Load when a client-data hook blocks a write, commit or push, before putting example data in docs/specs/comments/fixtures, or when real client data turns up in a committed file.
- [conventions/date-format.md](conventions/date-format.md) — `addSearch` needs `MM/DD/YYYY` for date fields; `YYYY/MM/DD` passes validation but silently fails to filter, and raw Java `LocalDate` objects also fail. Load when filtering a query by a date field.
- [conventions/deploy-lib-workflow.md](conventions/deploy-lib-workflow.md) — deploy an off-platform Vite/Preact SPA merge report with deploy-lib: install from git, the `package.json` `config` block (`deployUrl` is camelCase; `deploypathsuffix`/`builddir` lowercase), the `deploy` script, `npm run deploy -- --build --clean` (draft+snapshot upload), auth resolution (`--token-file` → `BLUESTEP_TOKEN` → `.env-local` → interactive, Bearer), Node 20+. Load when wiring or running a deploy-lib deploy for a `static/` SPA bundle.
- [conventions/endpoint-approach.md](conventions/endpoint-approach.md) — research-first: gather API docs, read type declarations, and study existing examples before writing an endpoint. Load when starting a new endpoint.
- [conventions/formula-patterns.md](conventions/formula-patterns.md) — correct BSJS formula patterns: form access, field reads/writes, HTTP, error handling, `B.*` utilities, DocumentLinkField. Load when writing a Formula or Post-Save script.
- [conventions/mcp-platform-authoring.md](conventions/mcp-platform-authoring.md) — the single shared procedure for a `[PLATFORM]` authoring/wiring op via the bundled platform-gateway MCP: connection-check (gateway tools live? fix = enable the `bluestep-tools` plugin + set `$B6PT_TOKEN` + restart) → resolve the target org to a U-number (user-supplied → `available_tenants` map → unlisted ≠ unreachable) → map op to an inner tool (optional `op:` hint), `list_org_tools` for schemas → approval echo of the concrete `invoke_org_tool` call (org + inner tool + args) → execute via `invoke_org_tool` → declaration read-back (`invoke_org_tool(tool:get_script_declarations)`) → idempotency detect-and-skip → destructive-tool discipline, plus the supported tool set, the **create-time display-column requirement** for queries/views (unfixable afterwards — the DELETE guard) and the **create-time completeness read-back**. `/spec-execute`, `/quick-task`, and free conversation all follow it. Load when about to add an import (query/form/field) or create a query/form/field/option-list/view/record-type via the gateway MCP — including raw `createRelateQuery` / `graphql_mutation`.
- [conventions/no-global-dollar.md](conventions/no-global-dollar.md) — never define a global `$` in MergeReport scripts; pages use jQuery and you will silently break Save and all page interactivity. Load when adding client JS to a MergeReport.
- [conventions/separate-files.md](conventions/separate-files.md) — don't pile CSS/HTML/JS into `app.ts`; use the `styles.css`/`index.html`/`script.ts` files the pulled folder already has. Load when structuring MergeReport files.
- [conventions/single-script.md](conventions/single-script.md) — only root `static/script.ts` → `.build/script.js` is ever loaded by the page (subdirectory `.ts` files compile but never load), and a server-side sibling under `scripts/` runs only through an ES import from `app.ts`. Load when splitting client scripts into multiple files, or reaching a sibling module from server code.
- [conventions/top-level-const-tdz.md](conventions/top-level-const-tdz.md) — endpoint/MergeReport scripts run top-down; declare every `const`/`let` above the entry `try` block or they are in TDZ when the handler fires. Load when ordering declarations in an endpoint or MergeReport.
- [conventions/ts-in-template-literal.md](conventions/ts-in-template-literal.md) — the inline `<script>` inside a `B.out` template literal must be plain JS (TS syntax ships verbatim and breaks parsing), and the literal cooks backslash escapes before shipping (`\s` → `s`), so embedded regexes must be double-escaped (`\\s`). Load when emitting client JS through a `B.out` string, or when client-side data is subtly wrong (e.g. a string one character short) while every server-side read is clean.

## gotchas/ — sharp edges

- [gotchas/b-exports-proxy-map.md](gotchas/b-exports-proxy-map.md) — `B.exports` is a Java-side proxy map (`ProxyJavaMap`), not a plain JS object; a value read back is a Java host object, so the accumulate pattern `B.exports.k = B.exports.k || []; B.exports.k.push(v)` type-checks locally but throws `Unknown identifier: push` at runtime; store structures as JSON strings (primitives round-trip). Load when passing data through `B.exports` between formulas/commit boundaries, especially arrays/objects.
- [gotchas/build-sourcemap-embeds-source.md](gotchas/build-sourcemap-embeds-source.md) — each compiled `draft/.build/*.js` carries the full TypeScript source as a base64 inline sourcemap, so text removed from the source (client data, a secret) stays in the repo until a publish (`/b6p-push`, on the user's choice) rebuilds `.build/` and a `b6p pull` brings it down; text search and the client-data checks cannot see it. Load when removing sensitive text from a component, or when a search of the source comes up clean but the text is still in the repo.
- [gotchas/common-gotchas.md](gotchas/common-gotchas.md) — anti-pattern roundup: Java optionals vs JS null checks, Java collections vs JS arrays, server/client separation, MergeReport HTML structure, SweetAlert2 v8 vs v11, ES feature limits, strict mode, query access, script-timing race, nullable booleans (an untouched checkbox is `null`, not `false`). Load when debugging a silent failure, before filtering records on a boolean flag, or before writing new component code.
- [gotchas/document-library-folder-keys.md](gotchas/document-library-folder-keys.md) — `Folder.subFolders()` / `subFoldersAsMap()` key each child by its name **plus a trailing slash** (`"Name/"`), so `subFolders()["Name"]` silently returns `undefined`; strip the slash before comparing or fetch via the real slash-suffixed key. Also: on a record, `documentLibrary()` and `recordFiles()` return the **same** root folder. Load when looking up a document-library subfolder by name, or working with `Folder`/document-library APIs.
- [gotchas/fetched-resource-code.md](gotchas/fetched-resource-code.md) — `FetchedResource.code()` returns `0` on success, not the upstream HTTP status; only `code >= 400` reliably indicates failure. Load when checking the result of a fetched resource.
- [gotchas/field-access-writability.md](gotchas/field-access-writability.md) — field access is independent grant ROWS: read and write are separate rows on the same field (`script_field_access` ADD with `writable:true` grants write only; a read+write field needs both), plus the form-level `script_forms writable` row; change a level by adding the new row before removing the old one (remove-first drops the field to zero access); verify with `list_field_access`, never the UI checkboxes or `list_applicable_fields`. Load when a field's writability disagrees with the UI, before wiring writable field access, or before changing a field's access level.
- [gotchas/meetscriteria-formula-context.md](gotchas/meetscriteria-formula-context.md) — `meetsCriteria()` on a unit-scoped query returns false in formula run context (on-demand/trigger) even for matching records — a silent false negative; gate on the record's own form entries there. Load when a formula gates on query membership, or `meetsCriteria` returns false unexpectedly.
- [gotchas/merge-report-primary-form-required.md](gotchas/merge-report-primary-form-required.md) — applicability lookups (`lookupMergeReportScript` / `optApplicable*`) return null/empty for a BSJS MergeReport with **no Primary Form assigned**, even with the applicability tag set. Load when an applicability lookup returns null or the embedding field renders `[no data]`.
- [gotchas/read-relatescript-source.md](gotchas/read-relatescript-source.md) — MCP `get_script`/`read_script_draft` error `FormulaModelRemote cannot be cast to … UserCodeScript` means "legacy RelateScript", not "missing"; read the source via the `editformuladetails.jsp` wizard (non-destructive). Load when reading or migrating a legacy RelateScript formula's source.
- [gotchas/relate-query-over-mefr.md](gotchas/relate-query-over-mefr.md) — reading multi-entry form data via a `view`-tool Relate query has four traps: `maxRows: 0` means literally zero rows (and is the default — use `-1`), `recordTypes` must be the record's BASE type (not the form's category), `list_views(formId:)` can't prove absence (omits MEFRs), and `relateQuery` can't execute an EntityList — use a `List` view for data reads (`create_mefr`'s EntityList is for script imports). Load when creating or querying a Relate view over a multi-entry form via MCP.
- [gotchas/third-party-lib-type-noise.md](gotchas/third-party-lib-type-noise.md) — a runtime-loaded client library (e.g. GridStack) with no local type declarations makes every push print advisory `Cannot find name` / `never` walls; harmless (the emit succeeds and the library exists at runtime in the browser), silence with an ambient `.d.ts` stub. Load when a push prints type errors about a third-party client-side global.
- [gotchas/vite-merge-report-gotchas.md](gotchas/vite-merge-report-gotchas.md) — sharp edges of the off-platform Vite/Preact SPA merge report: `base: './'` is mandatory (Vite defaults to `/`, assets 404 on-platform → blank render), `<head>` stripped but body `<script>`/`<link>`/mount-`<div>` survive, mount id must match, Node 20+ (`crypto is not defined` on 18), deploy-lib `deployUrl` camelCase casing, site CSS / `Swal` absent in local dev. Load when a Vite merge report renders blank/unstyled or a deploy fails.
- [gotchas/viewurl-is-javascript-call.md](gotchas/viewurl-is-javascript-call.md) — `entry().viewUrl()` returns `javascript:submitForm('Relate','commit','<url>')`, not a URL: `target="_blank"` is silently ignored and clicking COMMITS the enclosing form; unwrap the real URL with a regex before rendering. Load when rendering a link to a form entry, or when an entry link navigates same-tab / submits the form unexpectedly.
- [gotchas/option-field-dom-value.md](gotchas/option-field-dom-value.md) — a platform-rendered option field's DOM `value` is not reliably the export value (observed: the option's topId), so client code comparing DOM values against export codes silently never fires; read the live DOM first and match export value / topId / label defensively. Load before writing client code that reads a platform-produced DOM property, or when client logic keyed on option values never fires.
- [gotchas/connected-relationship-fields-inspector.md](gotchas/connected-relationship-fields-inspector.md) — a relationship field a formula never names can still show a `Formula write` edge in the dependency inspector, because writing one side of a relationship field writes the connected side too; not a stale edge, not evidence the formula references the field. Load when reading a dependency graph (Relate inspector or the gateway MCP inspector tool) that involves relationship fields.
- [gotchas/field-ids-not-portable-across-propagation.md](gotchas/field-ids-not-portable-across-propagation.md) — a form propagated from a Config org to descendant orgs does not carry matching field ids: a field built before a descendant org existed tends to match there, anything built after very likely diverges, and any match is coincidence. Load when platform work (field creation, wiring, any gateway MCP call carrying a topId) must run against more than one org in a propagation chain.
- [gotchas/unpublished-presave-draft-blocks-component.md](gotchas/unpublished-presave-draft-blocks-component.md) — a BsJs component on a form cannot be exercised while that form's pre-save formula has unpublished draft edits; the save fails with a bare, prefix-less "problem storing the data" that looks like a defect in the component under test. Load when a form save fails with no section prefix while testing an embedded component, or when a failure clears with no code change.
- [gotchas/merge-report-editability-is-component-level.md](gotchas/merge-report-editability-is-component-level.md) — a MergeReport has no per-field writability: one component-level box, "Allow this merge report to be used to edit data" under Advanced Usage Options, decides whether `mergeTag("F")` renders an editable control for every field at once; the `readonly` modifier in the declaration says nothing about it. Load before wiring an editable MergeReport field, or when asked to make a MergeReport field "writable".

## Term-level discovery

When no entry above matches, grep this skill's directory for the exact symbol or term:

```bash
grep -ri "<term>" "${CLAUDE_PLUGIN_ROOT}/skills/bluestep-reference/"
```

## Unresolved conflicts

Cross-source disagreements left for human resolution. Each is flagged inline with a `<!-- CONFLICT: … -->` comment at the point of disagreement; grep with `grep -rn "CONFLICT" "${CLAUDE_PLUGIN_ROOT}/skills/bluestep-reference/"`.

- [reference/api-patterns.md](reference/api-patterns.md) — client-ID field key: `sysId` (one reference) vs `systemID` (some merge-report examples). May be form/org-specific; needs confirmation of the canonical key.
- [b6p-platform.md](b6p-platform.md) — `U######` identity: treated here as the Unit folder created by `b6p pull` vs. another platform-overview source labeling it the Organization's own ID.
