@bluestep-systems/bspecs 0.10.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.
Files changed (83) hide show
  1. package/README.md +129 -0
  2. package/cli.js +74 -0
  3. package/package.json +30 -0
  4. package/src/prompts.js +74 -0
  5. package/src/scaffold.js +152 -0
  6. package/src/sync.js +123 -0
  7. package/src/utils.js +95 -0
  8. package/templates/claude/agents/b6p-code-review.md +81 -0
  9. package/templates/claude/agents/b6p-commenter.md +59 -0
  10. package/templates/claude/agents/b6p-task-implementer.md +77 -0
  11. package/templates/claude/hooks/block-generated-files.sh +16 -0
  12. package/templates/claude/hooks/block-tsc.sh +16 -0
  13. package/templates/claude/hooks/prettier-on-save.sh +21 -0
  14. package/templates/claude/instructions/b6p-platform.md.template +185 -0
  15. package/templates/claude/instructions/bsjs-development.md.template +430 -0
  16. package/templates/claude/instructions/conventions/always-snapshot.md.template +25 -0
  17. package/templates/claude/instructions/conventions/blueiq-no-ai-branding.md.template +11 -0
  18. package/templates/claude/instructions/conventions/date-format.md.template +27 -0
  19. package/templates/claude/instructions/conventions/endpoint-approach.md.template +9 -0
  20. package/templates/claude/instructions/conventions/formula-patterns.md.template +71 -0
  21. package/templates/claude/instructions/conventions/no-global-dollar.md.template +9 -0
  22. package/templates/claude/instructions/conventions/push-inner-draft.md.template +21 -0
  23. package/templates/claude/instructions/conventions/separate-files.md.template +17 -0
  24. package/templates/claude/instructions/conventions/single-script.md.template +28 -0
  25. package/templates/claude/instructions/conventions/snapshot-integrity.md.template +23 -0
  26. package/templates/claude/instructions/conventions/top-level-const-tdz.md.template +33 -0
  27. package/templates/claude/instructions/conventions/ts-in-template-literal.md.template +48 -0
  28. package/templates/claude/instructions/conventions/tsc-rootdir.md.template +17 -0
  29. package/templates/claude/instructions/gotchas/common-gotchas.md.template +91 -0
  30. package/templates/claude/instructions/gotchas/fetched-resource-code.md.template +9 -0
  31. package/templates/claude/instructions/index.md.template +82 -0
  32. package/templates/claude/instructions/reference/api-patterns.md.template +487 -0
  33. package/templates/claude/instructions/reference/blueiq-credit-integration-playbook.md.template +31 -0
  34. package/templates/claude/instructions/reference/chronounit-months.md.template +37 -0
  35. package/templates/claude/instructions/reference/code-patterns.md.template +265 -0
  36. package/templates/claude/instructions/reference/component-library.md.template +217 -0
  37. package/templates/claude/instructions/reference/crm-dashboard-inspo.md.template +17 -0
  38. package/templates/claude/instructions/reference/csv-parsing.md.template +18 -0
  39. package/templates/claude/instructions/reference/dashboard-design-system.md.template +38 -0
  40. package/templates/claude/instructions/reference/datetime-field-write.md.template +27 -0
  41. package/templates/claude/instructions/reference/design-system.md.template +150 -0
  42. package/templates/claude/instructions/reference/dpn-dashboard-framework.md.template +29 -0
  43. package/templates/claude/instructions/reference/endpoint-method-call.md.template +10 -0
  44. package/templates/claude/instructions/reference/endpoint-no-delete-method.md.template +9 -0
  45. package/templates/claude/instructions/reference/endpoint-output-channel.md.template +23 -0
  46. package/templates/claude/instructions/reference/endpoint-urls.md.template +15 -0
  47. package/templates/claude/instructions/reference/entry-delete.md.template +40 -0
  48. package/templates/claude/instructions/reference/file-execution.md.template +113 -0
  49. package/templates/claude/instructions/reference/http-requester.md.template +37 -0
  50. package/templates/claude/instructions/reference/id-full-vs-short.md.template +15 -0
  51. package/templates/claude/instructions/reference/internal-loopback-fetch.md.template +24 -0
  52. package/templates/claude/instructions/reference/localdate-parse.md.template +16 -0
  53. package/templates/claude/instructions/reference/merge-report-memo-json.md.template +25 -0
  54. package/templates/claude/instructions/reference/merge-report-static-index.md.template +29 -0
  55. package/templates/claude/instructions/reference/merge-report-urls.md.template +67 -0
  56. package/templates/claude/instructions/reference/multi-entry-in-multi-entry.md.template +21 -0
  57. package/templates/claude/instructions/reference/named-controls-submit.md.template +11 -0
  58. package/templates/claude/instructions/reference/new-entry-id.md.template +30 -0
  59. package/templates/claude/instructions/reference/relationship-field-set.md.template +37 -0
  60. package/templates/claude/instructions/reference/send-message-abort.md.template +37 -0
  61. package/templates/claude/instructions/reference/session-cookie-forwarding.md.template +31 -0
  62. package/templates/claude/instructions/reference/singleselect-null-copy.md.template +21 -0
  63. package/templates/claude/instructions/reference/staff-query-permission-gating.md.template +27 -0
  64. package/templates/claude/instructions/reference/timefield-vs-datetimefield.md.template +13 -0
  65. package/templates/claude/instructions/reference/user-zone-id.md.template +16 -0
  66. package/templates/claude/settings.json.template +46 -0
  67. package/templates/claude/skills/b6p-audit/SKILL.md +82 -0
  68. package/templates/claude/skills/b6p-pull/SKILL.md +123 -0
  69. package/templates/claude/skills/b6p-push/SKILL.md +70 -0
  70. package/templates/claude/skills/bug-fix/SKILL.md +28 -0
  71. package/templates/claude/skills/spec-create/SKILL.md +60 -0
  72. package/templates/claude/skills/spec-execute/SKILL.md +51 -0
  73. package/templates/claude/skills/spec-status/SKILL.md +20 -0
  74. package/templates/claude/skills/task-comment/SKILL.md +96 -0
  75. package/templates/claude/spec-templates/design.template.md +36 -0
  76. package/templates/claude/spec-templates/requirements.template.md +26 -0
  77. package/templates/claude/spec-templates/tasks.template.md +37 -0
  78. package/templates/module/README.md.template +46 -0
  79. package/templates/root/.gitignore.template +14 -0
  80. package/templates/root/.prettierrc.template +8 -0
  81. package/templates/root/CLAUDE.md.template +157 -0
  82. package/templates/root/README.md.template +58 -0
  83. package/templates/root/package.json.template +15 -0
@@ -0,0 +1,150 @@
1
+ # BlueStep Design System
2
+
3
+ ## Contents
4
+
5
+ - [Overview](#overview)
6
+ - [Component Source Priority](#component-source-priority)
7
+ - [Design Standards](#design-standards)
8
+ - [Standard Components](#standard-components)
9
+ - [Bootstrap Components](#bootstrap-components)
10
+ - [Using the Design System](#using-the-design-system)
11
+ - [Related Documentation](#related-documentation)
12
+ - [Access](#access)
13
+
14
+ > **Primary Reference**: The complete BlueStep Design System is available as an interactive merge report at:
15
+ > **https://bluestepplatformsupport.bluestep.net/shared/layouts/singleblock.jsp?_event=view&_id=120130___195147**
16
+
17
+ ## Overview
18
+
19
+ The BlueStep Design System is a comprehensive library of reusable components, design standards, and guidelines for BlueStep.js development. It serves three main purposes:
20
+
21
+ 1. **Establish clear standards** - Provide consistent design and development patterns for designers and developers
22
+ 2. **Component reference** - Serve as a comprehensive reference for UI components for both developers and AI agents
23
+ 3. **Rapid prototyping** - Enable AI agents to quickly create mockups based on user instructions
24
+
25
+ ## Component Source Priority
26
+
27
+ When creating mockups and developing software, components should be sourced in the following priority order:
28
+
29
+ 1. **BlueStep Generic Components** - Use components from the genericComponents library first (e.g., `moduleF`, `headF`, `svgF`, `tableF`)
30
+ 2. **Bootstrap 3.3.7 Components** - Use standard Bootstrap components when no generic component exists (e.g., buttons, alerts, modals, panels)
31
+ 3. **Custom Components** - Create from scratch only when necessary, inferring styles from the above sources to maintain consistency
32
+
33
+ This priority ensures consistency across BlueStep applications and leverages existing, tested components whenever possible.
34
+
35
+ ## Design Standards
36
+
37
+ The design system includes comprehensive documentation for:
38
+
39
+ ### Color Palette
40
+
41
+ **Standard Colors (CSS Variables)** - Available to all BlueStep pages:
42
+ - `--bs-red: #c03b2b`
43
+ - `--bs-orange: #F29D1F`
44
+ - `--bs-yellow: #EFC319`
45
+ - `--bs-green: #28AE60`
46
+ - `--bs-blue: #2A81BA`
47
+ - `--bs-logo-blue: #0063A6` (Brand logo color, used in email templates)
48
+ - `--bs-purple: #894C9E`
49
+ - `--bs-gray: #969FA0`
50
+ - `--bs-default: #2D3F50` (Default theme color)
51
+
52
+ **Custom Generated Colors (Style-Based)** - Generated based on currently selected color style:
53
+ - `--bs-primaryColor`
54
+ - `--bs-primaryColorHigh`
55
+ - `--bs-accent1Color`
56
+ - `--bs-accent2Color`
57
+ - `--bs-secondaryColor`
58
+ - `--bs-secondaryColorHigh`
59
+
60
+ > **Note**: Custom generated colors vary based on the organization's color style selection. Always use CSS variables for colors to ensure consistency.
61
+
62
+ ### Typography
63
+
64
+ The design system documents standard typography patterns, font sizes, and text styling conventions for BlueStep applications.
65
+
66
+ ### Spacing Scale
67
+
68
+ Standard spacing values and guidelines for consistent layout spacing throughout BlueStep applications.
69
+
70
+ ## Standard Components
71
+
72
+ The design system provides comprehensive documentation for all BlueStep generic components:
73
+
74
+ - **SVG Icons** (`svgF` / `svgIconF`) - Icon generation with size, color, and modifier options
75
+ - **Headers** (`headF`) - Standard and collapsible section headers
76
+ - **Tables** (`tableF`) - Data table generation
77
+ - **Fields** (`fieldF`) - Form field formatting
78
+ - **Bootstrap Forms** (`bootstrapFormF`) - Complete form generation
79
+ - **Modules** (`moduleF`) - Full page layouts with tabs
80
+ - **Generic HTML Tags** (`tagF`) - Custom HTML element generation
81
+ - **Anchor Tags** (`aF`) - Link generation
82
+ - **Email Templates** (`emailF`) - Responsive email generation
83
+ - **Breadcrumbs** (`breadcrumbF`) - Navigation breadcrumbs
84
+ - **Search Components** (`searchHeadF`, `searchTextF`) - Search interface components
85
+ - **Date Input** (`bsDateInput`) - Date picker components
86
+ - **Date Range** (`jsDateRange`) - Date range selection
87
+ - **Popin Hints** (`popinHintF`) - Contextual help tooltips
88
+ - **SVG with Title** (`svgTitleF`) - Icons with text labels
89
+ - **Horizontal Form Layout** (`bsHorizFormCol2F`, `bsHorizFieldCol2F`) - Two-column form layouts
90
+
91
+ Each component includes:
92
+ - Description and use cases
93
+ - Code examples
94
+ - Parameter documentation
95
+ - Visual examples
96
+ - Usage guidelines
97
+ - Accessibility notes
98
+
99
+ ## Bootstrap Components
100
+
101
+ The design system also documents standard Bootstrap 3.3.7 components:
102
+
103
+ - Buttons
104
+ - Alerts
105
+ - Badges
106
+ - Labels
107
+ - Panels
108
+ - Wells
109
+ - Modals
110
+ - Tooltips
111
+ - Popovers
112
+ - Tabs
113
+ - Accordion
114
+ - Pagination
115
+
116
+ ## Using the Design System
117
+
118
+ ### For AI Agents
119
+
120
+ When creating mockups or building BlueStep.js applications:
121
+
122
+ 1. **Reference the design system first** - Check the interactive design system merge report for component examples and patterns
123
+ 2. **Follow component priority** - Use BlueStep generic components before Bootstrap, and Bootstrap before custom HTML
124
+ 3. **Use design standards** - Apply color palette, typography, and spacing scale consistently
125
+ 4. **Check component documentation** - Review parameter lists, examples, and usage guidelines in the design system
126
+
127
+ ### For Developers
128
+
129
+ 1. **Browse the interactive design system** - Use the merge report to see live examples of all components
130
+ 2. **Copy code examples** - Use the provided code examples as starting points
131
+ 3. **Follow usage guidelines** - Adhere to accessibility and best practice recommendations
132
+ 4. **Reference component API** - Check the design system for parameter documentation
133
+
134
+ ### For Designers
135
+
136
+ 1. **Review design standards** - Understand color palette, typography, and spacing conventions
137
+ 2. **Explore component library** - See all available components and their visual appearance
138
+ 3. **Understand component capabilities** - Know what's possible with existing components before requesting custom solutions
139
+
140
+ ## Related Documentation
141
+
142
+ - **Component Library Usage** - See `agents-support/component-library.md` for quick reference on common components
143
+ - **Component Source** - See `https://files.bluestep.net/script/530024___41/static/genericComponents.js` for complete API documentation
144
+ - **Icon Reference** - See the BlueStep Icon Reference for complete icon list and categories
145
+
146
+ ## Access
147
+
148
+ **Design System URL**: https://bluestepplatformsupport.bluestep.net/shared/layouts/singleblock.jsp?_event=view&_id=120130___195147
149
+
150
+ The design system is also available in the workspace at `U129801/BlueStep.js Components` for local reference.
@@ -0,0 +1,29 @@
1
+ ---
2
+ description: "Parameterized data-endpoint + dashboard framework on rop — endpoint serves per-client/date-range JSON via URL params, dashboard fans out throttled parallel fetches; reference architecture for client-fed analytics dashboards"
3
+ ---
4
+
5
+ DPN Scoring framework on **rop** — a reference architecture for a dashboard that pulls per-entity, per-date-range data from a parameterized JSON endpoint. Two files, server-thin/client-fat. **Do not modify — reference only.**
6
+
7
+ - **Endpoint** `/b/dpnScoring` (file 1471219, key 363769___463) — the data service.
8
+ - **Merge report** DPN Dashboard (file 1475679, key 530024___1162) — the consumer/UI.
9
+
10
+ ## Endpoint: parameterized data service
11
+ `scripts/app.ts` reads four URL params and returns JSON. This is the part Brandon flagged — **same endpoint, many shapes of data via params**:
12
+
13
+ - `clientid` — which entity (accepts short id OR full `Class___Short`; resolved via `allActiveDischarge.optById(clientId)`).
14
+ - `startdate` / `enddate` — ISO `YYYY-MM-DD` window. Per-form entries are filtered with a plain string compare (`dateStr < startDate || dateStr > endDate`) since ISO sorts lexically. Top-level group-notes query instead uses `addSearch('groupNote','date','>=', MM/dd/yyyy)` — note the **format flip to MM/dd/yyyy** for `addSearch` (see [date format](../conventions/date-format.md)), wrapped in try/finally with `clearSearchAndSort()`.
15
+ - `action` — the response mode discriminator: `daily` (default; per-day buckets), `weekly` (Mon–Sun aggregated week rows with phase-weighted scores), or `full` (daily buckets + weeks). One endpoint, three payload shapes.
16
+
17
+ Response envelope is always `{ success, clientId, startDate, endDate, ... }` with `sendError(status, msg)` emitting `{ success:false, error }`. Missing params → 400 with a list of which were missing. Whole body in try/catch → 500. Reads via a query/field manifest (queries `allActiveDischarge` + `allGroupNotes`, each form listing the exact fields to load) — this component predates the move to platform form-import config and still uses a legacy `objects/imports.ts` `require` manifest; configure new components on the platform instead (see [api-patterns](api-patterns.md#query-access-patterns)). `config.json`: `transactionReadonly:false`, `transactionTimeout:3600`, `sandbox:SMALL`. Output via `response.out(JSON.stringify(...))` (see [endpoint output channel](endpoint-output-channel.md)).
18
+
19
+ ## Merge report: dashboard consumer
20
+ `scripts/app.ts` (server) emits **only a roster** — a `<script type="application/json">` of `{clientId, firstLast, team}` for every active client — plus the mount div, Chart.js CDN, and `.build/script.js`. No per-client data server-side; `record.id().toString()` (full form) is the wire id the endpoint expects.
21
+
22
+ `static/script.ts` (client) is where the framework lives:
23
+ - On load, default window = **last 4 full Mon–Sun weeks**; date inputs + Apply let the user change it.
24
+ - `loadWeeklyForAll()` builds one fetch task per roster entry and runs them through `runWithConcurrency(tasks, 10)` — a **throttled parallel pool (10 concurrent)** with a progress bar. This is the key pattern: dashboard does N calls to the same endpoint, one per client, varying `clientid` but sharing the date window.
25
+ - `fetchWeeklyForClient` / `fetchFullForClient` build `/b/dpnScoring?clientid=…&startdate=…&enddate=…&action=weekly|full`. Weekly drives the overview (stoplight bar, component lines, phase doughnut, sortable client table with sparklines, flagged bands). Full is **lazy-fetched** only when a client is opened (radar of current-week domains), cached in `state.fullByClient`.
26
+ - Changing the date range clears caches and refetches everything.
27
+
28
+ ## The reusable framework, in one line
29
+ A stateless endpoint keyed by `(entity id, date range, action)` returning a `{success,...}` JSON envelope; a thin server that ships only an entity roster; a fat client that fans out throttled-parallel per-entity fetches over a user-chosen window, caches by id, and lazy-loads detail. Build on [merge report memo json](merge-report-memo-json.md)-style server-thin/client-fat but feed the client from a params endpoint instead of a hidden memo.
@@ -0,0 +1,10 @@
1
+ ---
2
+ description: B.net.request.method is a method (call it), not a property — calling without parens type-checks but returns the Function reference at runtime
3
+ ---
4
+ `B.net.request.method` is typed as `method(): string` in B.d.ts — call it as a function: `B.net.request.method()`.
5
+
6
+ If you write `B.net.request.method` (no parens), TypeScript happily lets it through (the function reference is a truthy value), but at runtime you'll be comparing the Function object against `"GET"` and your method gate will never trigger.
7
+
8
+ Same pattern likely applies to other `request.*` accessors that look property-shaped — check the .d.ts before assuming.
9
+
10
+ Confirmed live on alpineacademy `/b/greatHair` endpoint, May 2026.
@@ -0,0 +1,9 @@
1
+ ---
2
+ description: "BlueStep endpoints don't route the DELETE (or likely other non-GET/POST/PUT) HTTP method — use a PUT/POST action discriminator instead"
3
+ ---
4
+
5
+ BlueStep `/b/<alias>` endpoints do **not** dispatch the `DELETE` HTTP method to the script. A `fetch(url, {method:'DELETE'})` never reaches the handler — the platform returns a non-JSON error page, so client-side `r.json()` throws and you see a generic "Network error" (NOT your handler's JSON error). Symptom: delete fails with "Network error — please try again" while POST/PUT/GET actions on the same endpoint work fine.
6
+
7
+ Fix: route mutations through `PUT` (or `POST`) with an `action` discriminator in the JSON body — e.g. `{action:'delete', entryId}` handled by `else if (action === 'delete')` in the `method === 'PUT'` branch. This is the same proven path as complete/reassign/completeWithResult on the WCWF endpoint (havenwoodslc/1523200). Confirmed GET/POST/PUT route; DELETE does not. (Likely PATCH/OPTIONS/etc. also don't — stick to GET/POST/PUT.)
8
+
9
+ Relates to [endpoint method call](endpoint-method-call.md) (request.method() must be called with parens), tracer audit.
@@ -0,0 +1,23 @@
1
+ ---
2
+ description: "/b/ endpoints write their body via B.net.response.out(string), NOT B.out — B.out is the merge-report channel and yields a blank 'Error' page on an endpoint"
3
+ ---
4
+
5
+ A BlueStep **`/b/<alias>` endpoint** must write its response body with **`B.net.response.out(stringBody)`** — a method on the Response object. Assigning **`B.out = ...`** does NOT work for an endpoint: `B.out` is the **merge-report** output channel, and using it on a `/b/` endpoint produces BlueStep's generic blank **"Error"** page (the script runs fine, but no endpoint output is ever emitted, so the framework reports failure). The same script type compiles either way — this is a runtime/routing distinction, not a type error.
6
+
7
+ **Endpoint output pattern (confirmed working — alpine RISE endpoint 1515139, and the BEH finance data endpoint /b/financeData):**
8
+ ```js
9
+ function writeJson(res, status, body) {
10
+ try { res.status(status); } catch (_e) {} // may throw if already committed
11
+ try { res.contentType("application/json; charset=UTF-8"); } catch (_e) {}
12
+ res.out(JSON.stringify(body)); // <-- the body channel
13
+ }
14
+ // entry:
15
+ const res = B.net.response;
16
+ const method = String(B.net.request.method() || "GET").toUpperCase(); // method() is a function — see endpoint method call
17
+ // ...dispatch on B.net.request.optParameter('x').orElse('')...
18
+ writeJson(res, 200, envelope);
19
+ ```
20
+
21
+ Wrap `status()`/`contentType()` in try/catch (they throw `IllegalStateException` once the response is committed). `res.out()` takes the full string body. Read request via `B.net.request` (`.method()`, `.optParameter(name).orElse(...)`, `.parameter(name)`). Endpoints route GET/POST/PUT but not DELETE ([endpoint no delete method](endpoint-no-delete-method.md)); the friendly `/b/<alias>` is configured per-endpoint, not derivable from the script name ([endpoint urls](endpoint-urls.md)).
22
+
23
+ Contrast: a **merge report** (e.g. the behavioral scorecard) renders via `B.out = htmlString`. So: merge report → `B.out`; `/b/` endpoint → `B.net.response.out(...)`.
@@ -0,0 +1,15 @@
1
+ ---
2
+ description: BlueStep endpoints are reached at /b/<alias>, not the /files/{id}/draft/ path — the alias is configured per-endpoint and is the URL to give users for testing
3
+ ---
4
+ BlueStep endpoint scripts have a configured **friendly URL** at `/b/<alias>` that is what users actually hit in the browser. The `/files/{id}/draft/` path is the editor view, not the runtime endpoint.
5
+
6
+ Example: Sprint Maestro (file id `1434036`, script name "Sprint Maestro") is exposed at `https://beh.bluestep.net/b/sprints?action=team`, not `https://beh.bluestep.net/files/1434036/draft/?action=team`.
7
+
8
+ The alias is set per-endpoint in BlueStep admin and is not derivable from the script name alone — also not necessarily plural-vs-singular consistent (Sprint Maestro → `sprints`).
9
+
10
+ **Symptom of a wrong alias:** the URL returns HTTP 500 with body literally just `Error` (BlueStep's generic page for "no endpoint at that path"). Status 500 + "Error" is NOT necessarily a script-side crash — first verify the alias before debugging the script.
11
+
12
+ **How to apply:**
13
+ - When reporting test URLs to the user, ask what the friendly alias is — do not guess from the script name or fall back to the `/files/.../draft/` URL.
14
+ - The `.b6p_metadata.json` `url` field is the editor URL, not the runtime URL.
15
+ - If the user reports "500 / Error" on an endpoint, ask them to confirm the alias before adding script-side error handling.
@@ -0,0 +1,40 @@
1
+ ---
2
+ description: Correct runtime API to delete a multi-entry-form entry — entry.entry().delete(), NOT entry.delete() — the typed declaration on the outer interface lies
3
+ ---
4
+ When you have a typed multi-entry-form record entry (e.g. `MEFR_sprints`, `MEFR_medicalTasks`), the typed `B.d.ts` claims `delete(): boolean` exists directly on the outer interface (around line 207-210 in current B.d.ts). **It doesn't, at runtime.** Calling it throws:
5
+
6
+ ```
7
+ TypeError: invokeMember (delete) on myassn.script.relate.FormRecordEntryScript failed due to: Unknown identifier: delete
8
+ ```
9
+
10
+ The actual API is to first unwrap to the underlying `FormEntry` via `.entry()`, then call `.delete()`:
11
+
12
+ ```typescript
13
+ // Correct
14
+ (entry as any).entry().delete();
15
+ B.commit();
16
+
17
+ // Wrong — runtime "Unknown identifier: delete"
18
+ entry.delete();
19
+ B.commit();
20
+ ```
21
+
22
+ `(entry as any)` is needed because the typed interface doesn't expose `.entry()` consistently across MEF entry types — but it exists at runtime on every form-entry-script object.
23
+
24
+ ## Existing usage to confirm pattern
25
+
26
+ Real BlueStep scripts that use this pattern (all snapshotted and live):
27
+ - `alpineacademy/Parent Opportunity API/draft/scripts/app.ts:82` — `entry.entry().delete()`
28
+ - `summitridge/Videra Proxy-Login/draft/scripts/app.ts:206` — `newNote.entry().delete()`
29
+ - `summitridge/1469686/draft/scripts/app.ts:779` — `entryOpt.get().entry().delete()`
30
+ - `alpineacademy/1458534/draft/scripts/app.ts:183` — `appt.entry().delete()`
31
+ - `summitridge/Report System/Report Data Maestro/draft/scripts/app.ts:825`
32
+
33
+ Apparently universal — the typed `entry.delete()` may be aspirational or deprecated.
34
+
35
+ ## How to apply
36
+
37
+ - For any "delete this MEF entry" code, write `(entry as any).entry().delete()` (or `entry.entry().delete()` if your entry variable already has FormEntry typing). Call `B.commit()` afterward to persist.
38
+ - Don't trust the `delete(): boolean` signature in `B.d.ts` line ~210 — it lies.
39
+ - The error "Unknown identifier: delete" is the diagnostic sign you've hit this trap.
40
+ - This is for **deleting individual entries** of a multi-entry form. To delete a whole record, use `record.deleteRecord()` (line ~18490 in B.d.ts) — that one IS available at runtime.
@@ -0,0 +1,113 @@
1
+ # File Execution Model
2
+
3
+ ⚠️ **Critical**: understanding which files execute, and when, is essential for BlueStep.js development. The core rule: **a file only runs if it is an automatic entry point or is explicitly imported/referenced.** Files that are neither never execute.
4
+
5
+ ## Contents
6
+
7
+ - [Merge Reports](#merge-reports)
8
+ - [Endpoints and Formulas](#endpoints-and-formulas)
9
+ - [Workspace Sync](#workspace-sync)
10
+ - [Common Mistakes](#common-mistakes)
11
+
12
+ ## Merge Reports
13
+
14
+ ### Execution entry points
15
+
16
+ Only two files execute automatically:
17
+
18
+ 1. **`scripts/app.ts`** — server-side only. Executes on the server, can access `B` and BlueStep APIs, cannot access the DOM, and writes to `B.out` (rendered into the page).
19
+ 2. **`static/index.html`** — client-side only. Executes in the browser, can access the DOM, cannot access `B`, and must reference other client files explicitly.
20
+
21
+ ### File structure
22
+
23
+ ```
24
+ /scripts/app.ts ← SERVER-SIDE ONLY - executes on the server
25
+ /static/index.html ← CLIENT-SIDE ONLY - executes in the browser
26
+ /static/script.js ← CLIENT-SIDE ONLY - must be referenced by index.html
27
+ /static/styles.css ← CLIENT-SIDE ONLY - for custom HTML only; component-library output needs no extra CSS
28
+ /objects/*.ts ← imported by app.ts or other files
29
+ ```
30
+
31
+ All other files must be explicitly imported or referenced. Creating files that aren't imported = wasted code.
32
+
33
+ ### HTML structure
34
+
35
+ Merge-report HTML (`static/index.html`) is **embedded into an existing BlueStep page**, so it must NOT include `<!DOCTYPE html>`, `<html>`, `<head>`, or `<body>`. Include only external `<script>`/`<link>` references, the content markup, and the application script at the end.
36
+
37
+ Connect/Manage sites provide the full HTML structure, navigation, branding, user context, and layout framework. Your merge report provides content (via `B.out` from `scripts/app.ts`), client markup (from `static/index.html`), and client scripts/styles. The content is rendered inside the site's content container, not as a standalone page — it is constrained by the site's layout.
38
+
39
+ ```html
40
+ <!-- External libraries (optional) -->
41
+ <link rel="stylesheet" href="styles.css" />
42
+
43
+ <!-- Your content -->
44
+ <div id="app-container"></div>
45
+
46
+ <!-- Application script — use type="module" when script.ts uses ES imports (e.g. genericComponents) -->
47
+ <script type="module" src=".build/script.js"></script>
48
+ ```
49
+
50
+ > ⚠️ **`type="module"` is required** whenever `script.ts` uses ES `import` statements (including importing from `genericComponents`). Without it the browser throws `SyntaxError: Cannot use import statement outside a module` and the script will not run.
51
+
52
+ ## Endpoints and Formulas
53
+
54
+ ### Execution entry points
55
+
56
+ Only one file executes automatically:
57
+
58
+ 1. **`scripts/app.ts`** — server-side only. Executes on the server, handles the request/event, can access `B`, cannot access the DOM.
59
+
60
+ ### File structure
61
+
62
+ ```
63
+ /scripts/app.ts ← SERVER-SIDE ONLY - handles the request/event
64
+ /objects/*.ts ← imported by app.ts
65
+ ```
66
+
67
+ ### Query access pattern
68
+
69
+ Which queries are available in `app.ts` is set by the component's **form-import config on the platform**, regenerated into `declarations/index.d.ts` on `b6p pull`. A configured query is a **bare top-level variable** named after the query FID — directly iterable, no `.query()` call:
70
+
71
+ ```typescript
72
+ // app.ts — query is a top-level variable (no .query() needed)
73
+ const unit = topLevelUnit[0];
74
+ const unitName = unit.forms.unitInformation.fields.unitName.opt().orElse('');
75
+ topLevelUnit.forEach((record) => { /* ... */ });
76
+ ```
77
+
78
+ The legacy `objects/imports.ts` `.require()` registration did this in code; it is not used in current modules. For full query patterns (writable context, ad-hoc queries) plus the legacy detail, see [api-patterns](api-patterns.md#query-access-patterns) and [code-patterns](code-patterns.md#query-patterns).
79
+
80
+ ## Workspace Sync
81
+
82
+ Each project has a `.b6p_metadata.json` file tracking sync state between the local workspace and the BlueStep server: script metadata (name, org ID, WebDAV ID), per-file last-sync timestamps, and build state. The `draft/` directory holds the working version of the code; the `.build/` directories hold the compiled JavaScript output from the TypeScript source.
83
+
84
+ ## Common Mistakes
85
+
86
+ ### Creating files that aren't imported
87
+
88
+ ```typescript
89
+ // ❌ Wrong - objects/helpers.ts exists but is never imported → it never executes
90
+ // ✅ Correct - import it (in app.ts)
91
+ import { helperFunction } from './objects/helpers';
92
+ ```
93
+
94
+ ### Mixing server and client code
95
+
96
+ ```typescript
97
+ // ❌ Wrong - DOM access in server code (scripts/app.ts)
98
+ document.getElementById('myElement'); // ERROR: document is not defined
99
+ // ❌ Wrong - B object in client code (static/script.js)
100
+ const user = B.user; // ERROR: B is not defined
101
+ ```
102
+
103
+ ### Incorrect HTML structure
104
+
105
+ ```html
106
+ <!-- ❌ Wrong - full HTML document -->
107
+ <!DOCTYPE html><html><head><title>My Report</title></head><body><div>Content</div></body></html>
108
+
109
+ <!-- ✅ Correct - content and references only -->
110
+ <link rel="stylesheet" href="styles.css" />
111
+ <div>Content</div>
112
+ <script src=".build/script.js"></script>
113
+ ```
@@ -0,0 +1,37 @@
1
+ ---
2
+ description: Correct method names on B.net.httpRequester for non-GET calls — uses overloaded getter/setter style (.method(), .responseCode()), not Java-bean style (.setMethod, .getResponseCode)
3
+ ---
4
+ `B.net.httpRequester(url)` returns an object whose mutators and accessors share the same name (overloaded). The Java-bean-style `setX`/`getX` names do **NOT** exist and are a TypeScript error.
5
+
6
+ ```typescript
7
+ // Correct
8
+ const req = B.net.httpRequester(url);
9
+ req.method('POST'); // setter (string arg)
10
+ req.setHeader('Authorization', API_KEY); // setHeader IS the right name (no overload)
11
+ req.setHeader('Accept', 'application/json');
12
+ req.doRequest();
13
+ const status = req.responseCode(); // getter (no args) — returns number | null
14
+ const body = req.getContent('UTF-8'); // getContent IS the right name
15
+ ```
16
+
17
+ ```typescript
18
+ // Wrong — these do NOT exist
19
+ req.setMethod('POST');
20
+ req.getResponseCode();
21
+ ```
22
+
23
+ **Methods that follow the bean naming**: `setHeader`, `getContent`, `doRequest`. **Methods that use the overloaded style**: `method`, `responseCode`. The split is inconsistent — when in doubt, look at `B.d.ts` declarations.
24
+
25
+ ## Reading POST body in an endpoint
26
+
27
+ ```typescript
28
+ const bodyText = B.net.request.content() || ''; // returns string
29
+ const body = JSON.parse(bodyText);
30
+ ```
31
+
32
+ `request.content()` is the canonical accessor — confirmed in declarations, used by the existing Sprint Maestro endpoint.
33
+
34
+ **How to apply:**
35
+ - For DELETE / POST / PUT calls (e.g., ClickUp tag reconciliation, REST writes), use `req.method('POST')` not `req.setMethod('POST')`.
36
+ - For checking response status to handle 404-as-success (idempotent deletes), use `req.responseCode()` not `req.getResponseCode()`.
37
+ - If a TypeScript error says `Property 'setMethod' does not exist`, this memory is why.
@@ -0,0 +1,15 @@
1
+ ---
2
+ description: Id.toString() returns ClassID___ShortID (full id); shortId() returns just the trailing number. optById needs the full form when round-tripping freshly-created entries.
3
+ ---
4
+
5
+ `entry.id().toString()` returns the **full system id** in `ClassID___ShortID` form (e.g. `1000201__21584230_2178970123`). `entry.id().shortId()` returns just the trailing record number (`2178970123`).
6
+
7
+ Declared at `B.d.ts:14373-14379`: *"Returns the standard System ID. That is ClassID___ShortID."*
8
+
9
+ `query.optById(s)` accepts the full form reliably. Feeding it just a `shortId()` can produce a malformed reconstructed key with an empty ClassID segment (e.g. `1000201___1153030` — three underscores), and `FinderException: Could not load Model for key: ...` is thrown.
10
+
11
+ **Why this matters for endpoint round-trips:** when an endpoint creates a new entry and returns its id to the client for a follow-up call (e.g. document upload after student creation), return `id().toString()`, not `id().shortId()`. If the UI also needs the short form for display, return both.
12
+
13
+ Related: [new entry id](new-entry-id.md) (commit before reading shortId).
14
+
15
+ **How to apply:** any time an id is going to be passed back to a server for `optById` lookup, use the full id. Reserve `shortId()` for human-readable display only.
@@ -0,0 +1,24 @@
1
+ ---
2
+ description: "Endpoint-to-endpoint loopback within an org must use B.net.fetch on a relative path + credentials:true, NOT the external httpRequester"
3
+ ---
4
+
5
+ For a BlueStep endpoint to call another endpoint in the SAME org (e.g. /b/aiAudio → /b/aiCredits), use `B.net.fetch(relativePath, {...})` — NOT `B.net.httpRequester(absoluteUrl)`.
6
+
7
+ `httpRequester` dials the server's own public hostname over the network; the server can't hairpin back to itself (NAT/firewall), so `doRequest()` never connects: `responseCode()` returns null and `getContent()` throws `Cannot invoke "java.io.InputStream.read(...)" because "this.in" is null`.
8
+
9
+ `B.net.fetch` on a RELATIVE path (`"/b/aiCredits"`) routes through BlueStep's internal dispatcher — no public round-trip — and `credentials: true` carries the caller's session so a login-required target doesn't 403. Read the result via `res.code()` (FetchedResource HTTP status) and `B.io.fromInputStream(res, "UTF-8")` for the body.
10
+
11
+ ```js
12
+ const res = B.net.fetch("/b/aiCredits", {
13
+ method: "POST", credentials: true, followRedirects: false, enableErrorStream: true,
14
+ connectionTimeout: 10000, readTimeout: 15000,
15
+ headers: { "Content-Type": "application/json", "Accept": "application/json" },
16
+ body: JSON.stringify({ action: "check" })
17
+ });
18
+ const code = res.code(); // HTTP status
19
+ const text = B.io.fromInputStream(res, "UTF-8"); // body
20
+ ```
21
+
22
+ `followRedirects:false` makes an auth bounce show as a non-2xx code instead of a login-page 200; `enableErrorStream:true` lets you still read the body on a non-2xx. FetchParams uses `connectionTimeout`/`readTimeout` (note the names). Confirmed working 2026-06-10 on summitridge /b/aiAudio (1471299) → /b/aiCredits (1471659): both the credit `check` gate and the `log` write now succeed and the BlueIQ ledger records spend.
23
+
24
+ Complements [session cookie forwarding](session-cookie-forwarding.md) (the cookie-via-raw-header trick was for httpRequester loopbacks; prefer B.net.fetch + credentials instead). Related: [endpoint output channel](endpoint-output-channel.md), [http requester](http-requester.md) (httpRequester is still correct for EXTERNAL calls like api.openai.com), blueiq credit system.
@@ -0,0 +1,16 @@
1
+ ---
2
+ description: Java.Time.LocalDate is a TypeScript namespace only; for runtime LocalDate.parse() / now() / etc. use B.time.LocalDate
3
+ ---
4
+ For runtime LocalDate operations, use `B.time.LocalDate`:
5
+
6
+ ```ts
7
+ const d = B.time.LocalDate.parse("2025-01-01");
8
+ const today = B.time.LocalDate.now();
9
+ if (d.isAfter(today)) { ... }
10
+ ```
11
+
12
+ `Java.Time.LocalDate` exists in the .d.ts as a namespace for type annotations, but `Java.Time` is NOT a runtime symbol — calling `Java.Time.LocalDate.parse(...)` throws `Java is not defined` (or returns undefined). Same applies to `Java.Time.ZonedDateTime`, `Java.Time.LocalDateTime`, etc.
13
+
14
+ **Use `Java.Time.X` only as type annotation; use `B.time.X` at runtime.**
15
+
16
+ Confirmed on alpineacademy `/b/greatHair`, May 2026.
@@ -0,0 +1,25 @@
1
+ ---
2
+ description: "The \"merge report + memo field JSON hack\" — core framework for embedding a custom interactive widget on a BlueStep form that persists its state as JSON in a hidden memo field"
3
+ ---
4
+
5
+ The recurring **"merge report + memo field JSON hack"** — how Brandon and I repeatedly build custom interactive UI on a BlueStep form without creating real sub-forms/MEF entries. This is the foundational framework; [multi entry in multi entry](multi-entry-in-multi-entry.md) and the permission layer [staff query permission gating](staff-query-permission-gating.md) are built on top of it.
6
+
7
+ **The idea:** A BlueStep **merge report placed on a form** renders custom HTML/JS. All widget state is stored as a single JSON blob inside one **hidden memo (or JSON-memo) field** on that same form. The client hijacks the form's own Save to persist — no separate records, no MEF plumbing. One memo field = the entire data model.
8
+
9
+ **Architecture (server-thin / client-fat), as separate files in a pulled component:**
10
+ - `draft/scripts/app.ts` (server scriptlet) — thin. Gathers anything the client needs that requires server context (current user, permission flags, reference/query data), emits it as `<script id="server-data" type="application/json">{...}</script>` plus an empty mount `<div id="...-app"></div>`, assigned to `B.out`. Wrap everything in try/catch; on failure emit a safe payload so the client still degrades cleanly.
11
+ - `draft/static/script.ts` (client) — the engine. On boot it:
12
+ 1. Finds the memo via `document.querySelector('[data-fid="<fieldName>"]')` (it's a `<textarea>`).
13
+ 2. **Hides the memo field's row** (`field.closest('tr')` → `display:none`) so users never see raw JSON.
14
+ 3. Hydrates state from the live memo value (`memo.value`), falling back to server-emitted copy, else a default `{ schemaVersion, entries: [] }`.
15
+ 4. Monkey-patches `window.submitForm`: wraps the original so it writes `memo.value = JSON.stringify(state)` right before the native save runs. The platform then persists the memo normally.
16
+ 5. Renders custom UI into the mount; a single `mutate()` does state → syncToField → re-render.
17
+ - `draft/static/styles.css` — lean on native BlueStep classes (`table table-striped`, `btn`, `sectionHeader sectionRuleB`); custom prefixed classes only for layout + modals.
18
+
19
+ **Critical gotchas (all learned the hard way):**
20
+ - Injected controls must have **NO `name` attribute** — named controls get submitted with the form → "problem storing the data". See [named controls submit](named-controls-submit.md).
21
+ - Mount modals on `document.body` to escape the form's submit scope.
22
+ - Borrow native relate icons (`/static/.../relate-icons/pencil.svg`, `trash.svg`) and call `window.fixAllSVG()` after render to recolor them like native lists.
23
+ - Build pipeline / push rules: [tsc rootdir](../conventions/tsc-rootdir.md), [push inner draft](../conventions/push-inner-draft.md), [separate files](../conventions/separate-files.md), [always snapshot](../conventions/always-snapshot.md).
24
+
25
+ **Reference implementations:** kaizenacademy/1465899 "Treatment Plan Review Comments Display" (original inspo, has Domains + lock); kaizenacademy/1466219 "Protocol Update Table" (notes-only + permission gating). Related JSON-in-memo dashboards: [crm dashboard inspo](crm-dashboard-inspo.md), visit dashboard.
@@ -0,0 +1,29 @@
1
+ ---
2
+ description: BlueStep merge report with a static/ bundle renders static/index.html as the page; scripts/app.ts B.out is NOT injected — put the mount + config island in index.html and run the client on DOMContentLoaded
3
+ ---
4
+
5
+ For a BlueStep merge report that ships a `static/` bundle (`static/index.html` +
6
+ `static/.build/script.js` + `static/styles.css`), the **served page is
7
+ `static/index.html`** — its `<script src=".build/script.js">` is what loads the
8
+ client. The server `scripts/app.ts` `B.out` is **not injected into that page**, so
9
+ a mount node / server-data island placed in `B.out` never appears in the DOM.
10
+
11
+ Symptom we hit (summitridge Resources Hub merge report 1471799): client JS ran but
12
+ `document.getElementById('rh-root')` and the `#rh-config` blob were both null,
13
+ because the mount + config were emitted from `B.out`. Spinner hung forever.
14
+
15
+ **Do this instead:**
16
+ - Put the root mount (`<div id="rh-root">`) and any static config island
17
+ (`<script id="rh-config" type="application/json">…</script>`) **directly in
18
+ `static/index.html`**, before the `<script src=".build/script.js">` tag. (Mirror
19
+ of the working Treatment Targets widget: `<div id="tt-widget"></div>` lives in
20
+ index.html.)
21
+ - Run the client on DOM-ready: `if (document.readyState==='loading')
22
+ document.addEventListener('DOMContentLoaded', start); else start();`
23
+ - The client should fetch all dynamic data from its endpoint (server-thin/client-fat)
24
+ rather than expecting server-rendered data from `B.out`.
25
+ - Server-side per-user data (e.g. current user for a form prefill) can't ride in via
26
+ `B.out`; defer it, fetch it from the endpoint, or use index.html merge tokens.
27
+
28
+ Builds on [merge report memo json](merge-report-memo-json.md) and
29
+ [separate files](../conventions/separate-files.md); relevant to bluestep resources.
@@ -0,0 +1,67 @@
1
+ ---
2
+ description: BSJS equivalents for relatescript form.System.lookupMergeReport / lookupMergeReportScript and how to get viewUrl/printUrl
3
+ ---
4
+ # BSJS Merge Report URL Lookup
5
+
6
+ Relatescript provides `form.System.lookupMergeReport("id","x")` and `lookupMergeReportScript("id","x")` returning `{name, label, viewURL, printURL, customProps}`. BSJS splits this across two APIs.
7
+
8
+ ## Direct id lookup (no record applicability filter)
9
+
10
+ ```typescript
11
+ // Short id + lang ('js' BSJS, 'rs' relatescript):
12
+ B.find.mergeReport("shortId", "js").viewUrl()
13
+
14
+ // Full id, no lang:
15
+ B.find.mergeReport("530024__FID_clientHeader").viewUrl()
16
+ ```
17
+
18
+ Returns `MergeReportMetaData` (inherits `viewUrl()`, `editUrl()`, `id()`, `displayName()` from `BaseObject`). No `printUrl()` here.
19
+
20
+ ## Lookup by custom property, scoped to a record's form (true equivalent of `lookupMergeReport`)
21
+
22
+ ```typescript
23
+ // BSJS reports (= lookupMergeReportScript):
24
+ formEntry.optApplicableBsJsMergeReport("id", "x").get()
25
+
26
+ // Relatescript reports (= lookupMergeReport):
27
+ formEntry.optApplicableMergeReport("id", "x").get()
28
+ ```
29
+
30
+ Returns full `MergeReport<T>` with `viewUrl()`, `printUrl()`, `profileUrl()`, `newEntryUrl()`, `copyUrl()`, `editUrl()`, `displayName()`, `id()`, `altIds()`, etc.
31
+
32
+ Also: `applicableBsJsMergeReportResults(...)` returns `{results, message}` for error reporting.
33
+
34
+ ## Property mapping
35
+
36
+ | relatescript | BSJS |
37
+ |---|---|
38
+ | `result.viewURL` | `.viewUrl()` |
39
+ | `result.printURL` | `.printUrl()` (only on full `MergeReport`, not metadata) |
40
+ | `result.name` | `.displayName()` (or `.id()` for the id) |
41
+ | `result.label` | `.displayName()` |
42
+ | `result.customProps` | no enumerator — pass as filter args, or walk `.altIds()` |
43
+
44
+ ## Gotchas
45
+
46
+ - `viewUrl()` is **relative**. For absolute (emails etc.) wrap with `B.toFullyQualifiedUrl(rel)`.
47
+ - `B.siteNavigation()` returns `SiteNavItem` (pages) — **does NOT** have `optLookupMergeReport`. The `optLookup*MergeReport` methods are on `RecordNavItem`, obtainable via `record.nav()`, but `record.nav()` is documented as expensive (30+ sec on uncached orgs). Prefer `B.find.mergeReport` or `optApplicableBsJsMergeReport`.
48
+ - `B.find.mergeReport(id, 'js')` does NOT filter by record applicability — it's an id lookup. Only `optApplicable*` methods on FormEntry filter by applies-to-this-record.
49
+ - Both APIs work in `MergeReportB` and `CommitableB` contexts (read-only).
50
+ - **The `BsJs` prefix names the TARGET, not the caller.** Pick `optApplicableMergeReport` if the merge report you're looking up is a relatescript merge report — even when you're calling from a BSJS endpoint. Pick `optApplicableBsJsMergeReport` only if the target itself is a BSJS merge report. Confirmed in production on summitridge Report Viewer (Apr 2026): looking up the relatescript "unsignedForms" merge report from a BSJS CommitableB endpoint required `optApplicableMergeReport`, not the BsJs variant.
51
+
52
+ ## Existing examples in pulled code
53
+
54
+ - `summitridge/Settings Events/draft/scripts/app.ts:52-54, 81, 276` — `B.find.mergeReport(shortId, 'rs').id()` / `.displayName()`
55
+ - `alpineacademy/1463039/draft/scripts/medFunctions.ts:171` — `optApplicableMergeReport("id","medPrescribed").get().viewUrl()`
56
+ - `beh/1433876/draft/scripts/app.ts:167` — `BaseObject.viewUrl()` on a record
57
+
58
+ ## Declarations source
59
+
60
+ All in `declarations/B.d.ts`:
61
+ - `B.find.mergeReport`: lines 23984-24001
62
+ - `MergeReportMetaData`: line 11373
63
+ - `MergeReport<T>` (printUrl 8398, profileUrl 8408, newEntryUrl 8413, copyUrl 8423): line 8389
64
+ - `BaseObject.viewUrl()`: line 24613
65
+ - `FormEntry.optApplicableBsJsMergeReport`: line 7585
66
+ - `FormEntry.optApplicableMergeReport`: line 7573
67
+ - `RecordNavItem.optLookupBsJsMergeReport`: line 10292