@bluestep-systems/bspecs 0.13.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -98,7 +98,7 @@ my-project/
98
98
  ├── bspecs.lock ← lock file for bspecs sync
99
99
  ├── settings.json ← Claude Code permissions and hooks
100
100
  ├── hooks/ ← 3 scripts executed by Claude Code
101
- ├── skills/ ← 8 skills (/spec-create, /b6p-pull, etc.)
101
+ ├── skills/ ← 9 skills (/spec-create, /b6p-pull, /bspecs-feedback, etc.)
102
102
  ├── agents/ ← 3 BlueStep subagents (implementer, commenter, reviewer)
103
103
  ├── instructions/ ← development rules for Claude
104
104
  ├── spec-templates/ ← spec file templates
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bluestep-systems/bspecs",
3
- "version": "0.13.0",
3
+ "version": "0.15.0",
4
4
  "description": "Spec-driven BlueStep development with AI agents — scaffolder and project conventions for Claude Code",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,7 +10,8 @@ When a BlueStep pull contains dedicated files (`styles.css`, `index.html`, `scri
10
10
 
11
11
  - Before writing or modifying a BlueStep file, list the folder contents and identify which dedicated files exist.
12
12
  - Route content to its natural file: CSS → `styles.css`, markup template → `index.html` (or a separate template file), client-side JS → `script.ts`, server-side logic → `app.ts`.
13
- - For merge reports specifically: the server-side TS can read sibling files via `B.io.fromInputStream(...)` (or the appropriate API) and inject them into `B.out` investigate the existing API rather than inlining.
13
+ - For merge reports **with a `static/` bundle**: `static/styles.css` and `static/.build/script.js` load automatically while `B.out` renders the page body leave CSS in `styles.css`; do **not** read or `B.net.fetch` your own stylesheet to inline it. See [merge report static index](../reference/merge-report-static-index.md).
14
+ - For older merge reports **without** a `static/` bundle: the server-side TS can read sibling files via `B.io.fromInputStream(...)` (or the appropriate API) and inject them into `B.out` — investigate the existing API rather than inlining.
14
15
  - For endpoints with `static/` folders: HTML/CSS/JS load from their own files; `app.ts` is the request handler only.
15
16
  - If unsure how multiple files relate in a given BlueStep component type, check [api patterns](../reference/api-patterns.md) and look at how other working components are organized.
16
17
 
@@ -11,6 +11,7 @@ Entries read: `path — what it covers. Load when <trigger>.` Match the trigger
11
11
  ## reference/ — single-topic platform and API detail
12
12
 
13
13
  - [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, user/session. Load when calling the `B` API, reading or writing fields, or wiring an endpoint's request/response.
14
+ - [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.
14
15
  - [reference/blueiq-credit-integration-playbook.md](reference/blueiq-credit-integration-playbook.md) — step-by-step playbook for wiring a new OpenAI/AI feature into the BlueIQ credit gate and ledger. Load when adding an AI feature that must consume BlueIQ credits.
15
16
  - [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)`.
16
17
  - [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.
@@ -31,8 +32,9 @@ Entries read: `path — what it covers. Load when <trigger>.` Match the trigger
31
32
  - [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`.
32
33
  - [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.
33
34
  - [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.
35
+ - [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.
34
36
  - [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.
35
- - [reference/merge-report-static-index.md](reference/merge-report-static-index.md) — with a `static/` bundle, `static/index.html` is the page and `app.ts` `B.out` is NOT injected; put the mount + config island in `index.html` and run the client on `DOMContentLoaded`. Load when a MergeReport has a `static/` bundle.
37
+ - [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.
36
38
  - [reference/merge-report-urls.md](reference/merge-report-urls.md) — BSJS equivalents of relatescript `lookupMergeReport`/`lookupMergeReportScript`, and how to get `viewUrl`/`printUrl`. Load when looking up a MergeReport URL in code.
37
39
  - [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.
38
40
  - [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.
@@ -0,0 +1,46 @@
1
+ ---
2
+ description: "`<b-include>` is a browser custom element that fetches HTML from a URL and renders it inline — client-side `<jsp:include>`/SSI for dynamic async content; trusted-template only (neutralized in user-supplied HTML)"
3
+ ---
4
+
5
+ `<b-include>` is a **browser custom element** (defined in `dom.ts:432-560`) that
6
+ **fetches HTML from a URL and renders it inline** — think client-side `<jsp:include>`
7
+ / server-side-include for the browser. Use it for dynamic, async page fragments.
8
+
9
+ **Syntax** — must use an explicit closing tag (it is **not** self-closing):
10
+ ```html
11
+ <b-include src="/path/to/fragment.jsp"></b-include>
12
+ ```
13
+
14
+ **Attributes**
15
+
16
+ | Attribute | Values | Purpose |
17
+ | ------------- | --------------------- | ------- |
18
+ | `src` | URL | Content to fetch. **Reactive** — changing it re-fetches automatically. |
19
+ | `run-scripts` | `"true"` \| (omit) | When `"true"`, `<script>` tags in the fetched HTML are re-inserted so the browser executes them. Default: scripts are inert. |
20
+ | `csrf` | `"true"` \| `"false"` \| (omit) | Force CSRF handling. Omit for auto-detect: same-origin → `csrf.fetch`, cross-origin → plain fetch. |
21
+
22
+ **Examples**
23
+ ```html
24
+ <!-- Basic same-origin include (CSRF token auto-attached) -->
25
+ <b-include src="/shared/content/fragment.jsp"></b-include>
26
+
27
+ <!-- Include a fragment and run its <script> tags -->
28
+ <b-include src="/path/with/scripts.html" run-scripts="true"></b-include>
29
+
30
+ <!-- Cross-origin with forced CSRF off (remote must send CORS headers) -->
31
+ <b-include src="https://other.example.com/widget" csrf="false"></b-include>
32
+ ```
33
+
34
+ **Behavior**
35
+ - Shows a spinner while loading; shows an error box with a **"Reload"** link on failure.
36
+ - Re-setting `src` aborts the in-flight request and re-fetches.
37
+ - Removal from the DOM aborts any pending request.
38
+ - Drivable from JS via properties: `el.src`, `el.runScripts`, `el.csrf`.
39
+
40
+ **Security — trusted templates only.** `<b-include>` is **neutralized in user-supplied
41
+ content** by `HTMLFilter.java:46` (rewritten to `<xxb-includexx>`) to prevent injection.
42
+ Only use it in **trusted templates**, never in user-editable HTML. The Zesty editor
43
+ whitelists it as `b-include[src|run-scripts|csrf]` (`zesty3.ts:130`).
44
+
45
+ For the server-side config alternative (a merge report that lazy-loads after the page),
46
+ see [merge-report-async-loading](merge-report-async-loading.md).
@@ -9,7 +9,7 @@ A server-side BSJS merge report CAN access and parse an uploaded CSV at runtime.
9
9
  **Three ways to get the file's bytes (priority order):**
10
10
  1. **DocumentLinkField on a form/record (preferred)** — an uploaded-file field. `field.content()` → string; `field.forReader(r => B.csv(r).toListOfObjects())` streams it. Also `.forInputStream()`, `.toBytes()`, `.filename()`, `.contentType()`, `.permUrl()/.davUrl()`. No HTTP hop, no auth ambiguity.
11
11
  2. **Document in a folder** — `folder.documents()['name.csv']` → same `.content()/.forReader()/.toBytes()` accessors. For file-system uploads not on a record.
12
- 3. **Fetch by URL** — same `B.net.fetch(url)` pattern we use to inline styles.css; `B.csv(fetcher, fetcher.charset)`. Fallback only — reliability depends on URL + session auth. Prefer 1/2 for uploaded files.
12
+ 3. **Fetch by URL** — `B.net.fetch(url)` then `B.csv(fetcher, fetcher.charset)`. Fallback only — reliability depends on URL + session auth. Prefer 1/2 for uploaded files.
13
13
 
14
14
  **Permissions:** merge report runs in current user's context; routes 1/2 read via the platform object model subject to record/folder security.
15
15
 
@@ -0,0 +1,41 @@
1
+ ---
2
+ description: A Data Merge Report's "Asynchronous Loading" option makes it lazy-load AFTER the rest of the page resolves instead of as part of the initial request; in BSJS this was an `async` metadata property rather than the checkbox
3
+ ---
4
+
5
+ A BlueStep **Data Merge Report** has an **"Asynchronous Loading"** checkbox under
6
+ *Advanced Usage Options* (Step 3 of the merge-report edit wizard). Turning it on
7
+ makes the merge report **resolve in a lazy-load rather than as part of the initial
8
+ page request** — the rest of the page renders first, then the merge report loads
9
+ in separately so a heavy merge doesn't slow everything else down.
10
+
11
+ This is **not a new feature**. It maps directly to the underlying DB object and has
12
+ existed for a long time. **BSJS had no toggle** — you set an `async` property in the
13
+ component metadata, which inserted a layer of indirection over that same DB object.
14
+ Removing that BSJS layer is what surfaced the plain checkbox; it only *looks* new to
15
+ anyone who previously only used BSJS.
16
+
17
+ **Parent → child behavior.** When a merge report calls another async merge report,
18
+ the **parent's HTML resolves first**, the page loads, and then each async child
19
+ loads separately — so a parent merge report that fans out to N children can give
20
+ each child its own independent loader. (The resident-med report does exactly this:
21
+ a nasty merge that renders per resident, switched to async, **loads in batches of
22
+ 3 at a time**.)
23
+
24
+ **Good use cases:** heavy/slow merges; merge reports embedded on a form; a parent
25
+ merge report aggregating several child merge reports; query-driven layouts where
26
+ displayed merge-report fields can stream in after the header data.
27
+
28
+ **Gotcha — script timing.** Because the content is no longer present at initial page
29
+ load, any page script that **expects the async merge report's DOM/data to already be
30
+ there will break**. If you have client code depending on the merge report, gate it on
31
+ the async content actually arriving rather than on initial `DOMContentLoaded`.
32
+
33
+ **Obsolete hack.** Older setups added a marker like
34
+ `htmlCode += '<span id="formFooter"></span>'` to force the async merge report to load
35
+ properly. With the current setup this should **no longer be necessary** — don't carry
36
+ it forward into new code.
37
+
38
+ For dynamic async content driven from the client (rather than this server-side
39
+ config), see [b-include element](b-include-element.md). Related merge-report detail:
40
+ [merge-report-static-index](merge-report-static-index.md),
41
+ [merge-report-memo-json](merge-report-memo-json.md).
@@ -1,29 +1,52 @@
1
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
2
+ description: BlueStep merge report 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/markup/CSS in static/, use B.out only for final server-rendered markup; never fetch/inline your own styles.css and never use a B.out config island as a data channel to the index.html client script.
3
3
  ---
4
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.
5
+ In a BlueStep merge report that ships a `static/` bundle (`static/index.html` +
6
+ `static/.build/script.js` + `static/styles.css`), **both entry points render into
7
+ the page together** (see [file execution](file-execution.md)):
8
+
9
+ - `scripts/app.ts` writes **server content** to `B.out` this **does** render.
10
+ - `static/index.html` supplies the **client markup**; its `<link rel="stylesheet"
11
+ href="styles.css">` and `<script src=".build/script.js">` load **automatically**.
12
+
13
+ But the two are **completely disjoint**: `B.out` is injected as a tag on the page
14
+ that runs **after** `static/index.html` has been put on the page. They share no DOM
15
+ context you can bridge — a node emitted from `B.out` is **not** something the
16
+ index.html client script can reliably reach, and ordering tricks like
17
+ `DOMContentLoaded` won't fix it (B.out arrives later, in a separate region).
18
+
19
+ The platform serves `static/styles.css` (and `static/.build/script.js`) for you
20
+ while `B.out` renders the page body. So you do **not** need to read or fetch your
21
+ own stylesheet — `B.net.fetch("static/styles.css")` + inlining it into a `<style>`
22
+ block in `app.ts` is pointless work. CSS stays in `static/styles.css` per
23
+ [separate files](../conventions/separate-files.md).
24
+
25
+ **Where each piece goes:**
26
+ - Root mount (`<div id="…-root">`) and static config islands (`<script id="…-config"
27
+ type="application/json">…</script>`) → put in `static/index.html`, before the
28
+ `<script src=".build/script.js">` tag. (Mirror of the Treatment Targets widget:
29
+ `<div id="tt-widget"></div>` lives in index.html.)
30
+ - CSS → `static/styles.css`. Client JS → `static/script.ts` (→ `.build/script.js`).
31
+ - `B.out` (from `app.ts`) → **final server-rendered markup only**: HTML with
32
+ server-computed values baked straight in, e.g. record-scoped section URLs (see
33
+ [merge report urls](merge-report-urls.md)), current-user values, or query results
34
+ rendered as the visible content. Because B.out is disjoint from index.html, do
35
+ **not** emit a mount or a `<script type="application/json">` config island from
36
+ `B.out` and expect the index.html client script to read it — that's the trap below.
37
+
38
+ **Getting server data to the client widget:** when a client-side widget (booted by
39
+ the index.html script) needs server context, don't route it through `B.out` — fetch
40
+ it from the component's **endpoint** (server-thin/client-fat), or use **index.html
41
+ merge tokens**. Reserve `B.out` for content it renders directly.
10
42
 
11
43
  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.
44
+ `document.getElementById('rh-root')` and the `#rh-config` blob were both null
45
+ because the mount + config island were emitted from `B.out`, a separate region that
46
+ is injected *after* index.html and disjoint from the index.html client script. We
47
+ *wrongly* concluded `B.out` wasn't injected — it is. The fix is to put the mount +
48
+ config island in `static/index.html` (the client-markup file), and fetch any dynamic
49
+ data from the endpoint.
27
50
 
28
51
  Builds on [merge report memo json](merge-report-memo-json.md) and
29
52
  [separate files](../conventions/separate-files.md); relevant to bluestep resources.
@@ -0,0 +1,97 @@
1
+ ---
2
+ name: bspecs-feedback
3
+ description: Send a bspecs tooling-change request (a rule, skill, subagent, hook, instruction, spec/module template, or the CLI) to the canonical bspecs repo as a prefilled GitHub issue. Use when you or the user notice something about the bspecs tooling itself that should change — local `.claude/` edits do not survive `bspecs sync`, so the fix has to go upstream.
4
+ ---
5
+
6
+ # /bspecs-feedback — Send a tooling-change request upstream
7
+
8
+ This project's `.claude/` tree (skills, hooks, instructions, settings, this very file) is a **scaffolded copy** that `bspecs sync` overwrites. Editing it locally does not reach the canonical repo and does not survive the next sync. This skill routes feedback to **`github.com/Bluestep-Systems/bspecs`** as a prefilled GitHub issue, so a fix can land there and ship to every project.
9
+
10
+ It needs **no token and no backend**: the repo is public, the issue form is prefilled via a deep link, and GitHub authenticates the user through their existing browser session.
11
+
12
+ **Scope check first.** This skill is for the **bspecs tooling** (a skill is wrong, a hook misfires, an instruction template is misleading, a `CLAUDE.md` rule is stale, a missing capability), or for a B6P rule general enough to belong in **every** scaffolded project. For project-local B6P domain knowledge that only matters here, capture it locally instead (see the Self-improvement section of this project's `CLAUDE.md`).
13
+
14
+ ## Steps
15
+
16
+ ### 1. Draft from context — don't cold-quiz
17
+
18
+ The skill usually runs right after you and the user discussed the thing to change, so the relevant detail is already in the conversation. Draft the submission from it:
19
+
20
+ - **kind(s)** — one or more of: `add rule`, `change rule`, `remove rule`, `report error/bug`, `request capability`, `report friction`. Multi-valued — a misleading rule is often `report error/bug` **and** `change rule`.
21
+ - **target(s)** — one or more artifacts the feedback hits: instruction / skill / subagent / hook / settings-permission / spec template / module template / `CLAUDE.md` / CLI.
22
+ - **file_path**, **current_text**, **proposal** — see step 2.
23
+
24
+ Only when there is nothing to infer from (e.g. the user just types `/bspecs-feedback "the STOP messages are annoying"`) do you ask directly for kind(s) and target(s) and a one-line description.
25
+
26
+ ### 2. Capture context conditioned on kind
27
+
28
+ The kind set drives what you collect (pull from the conversation first; read the tree to fill gaps):
29
+
30
+ - **change rule / remove rule** → the affected **file path** + the **current rule text quoted verbatim** (read it from the `.claude/` tree). Keep the excerpt focused — do not paste a whole file.
31
+ - **report error/bug** → a repro: what was run, what happened, what was expected.
32
+ - **request capability** → the use case: what the user is trying to do that no skill/hook/CLI feature supports.
33
+ - **add rule** → where the guidance should live + the proposed text.
34
+ - **report friction** → what is painful and why (no fix required).
35
+
36
+ Always capture the **bspecs version** — read `bspecs_version` from `.claude/bspecs.lock`:
37
+
38
+ ```
39
+ node -e "try{const fs=require('fs');process.stdout.write(JSON.parse(fs.readFileSync('.claude/bspecs.lock','utf8')).bspecs_version||'unknown')}catch(e){process.stdout.write('unknown')}"
40
+ ```
41
+
42
+ (Read + `JSON.parse` explicitly — `require('./.claude/bspecs.lock')` does **not** work, because Node's `require` only resolves `.js`/`.json`/`.node`, not a `.lock` extension.)
43
+
44
+ Fall back to `unknown` if the lock is missing; never block on it.
45
+
46
+ ### 3. Confirm with the user
47
+
48
+ Show the drafted submission — kind(s), target(s), file path, and the proposal/rationale — and ask:
49
+
50
+ > File this to the bspecs repo?
51
+
52
+ Adjust whatever the user corrects. Do **not** file without explicit confirmation.
53
+
54
+ ### 4. Build the prefilled issue link
55
+
56
+ Target the structured issue form (`feedback.yml`) and prefill its fields by `id`, applying the `feedback` label. Assemble and URL-encode with node's `URLSearchParams` (node is guaranteed present — it backs `npx b6p`):
57
+
58
+ ```
59
+ node -e '
60
+ const fs=require("fs");
61
+ const f=JSON.parse(fs.readFileSync(process.argv[1],"utf8")); // {title, file_path, current_text, proposal, version}
62
+ const p=new URLSearchParams({template:"feedback.yml", labels:"feedback", ...f});
63
+ process.stdout.write("https://github.com/Bluestep-Systems/bspecs/issues/new?"+p.toString());
64
+ ' /path/to/fields.json
65
+ ```
66
+
67
+ Write the field values to a temp JSON file first (in the scratchpad) so multi-line `current_text` / `proposal` survive without shell-quoting pitfalls.
68
+
69
+ **Kind/target safety net.** The form's `kind` and `target` are multi-select dropdowns, and GitHub's query-param prefill for multi-selects is unreliable — and we use a **single `feedback` label** (no `kind:*` labels), so kind cannot ride the `labels=` param either. Therefore **embed kind(s) and target(s) as text** so the signal never gets lost:
70
+
71
+ - Put a short kind summary in the **title**, e.g. `[feedback] change rule: b6p-push auth preflight wording`.
72
+ - Lead the **proposal** field with a `Kind: … · Target: …` line, then the rationale/repro/use-case.
73
+
74
+ `current_text`, `proposal`, `title`, `file_path`, `version` prefill reliably (the textareas have no `render:` attribute, which is required for query-param prefill). The dropdowns are best-effort on top.
75
+
76
+ ### 5. Open it, and always print the URL
77
+
78
+ Open with the platform opener, falling back across environments, then echo the URL regardless:
79
+
80
+ ```
81
+ url="<assembled url>"
82
+ ( command -v wslview >/dev/null 2>&1 && wslview "$url" ) \
83
+ || ( command -v xdg-open >/dev/null 2>&1 && xdg-open "$url" ) \
84
+ || ( command -v open >/dev/null 2>&1 && open "$url" ) \
85
+ || true
86
+ echo "$url"
87
+ ```
88
+
89
+ If auto-open fails (headless, no opener), the printed URL is the fallback — the user clicks it. The user reviews the prefilled form in their browser and submits; GitHub handles auth. The form applies the `feedback` label on submit even if the `labels=` param is dropped.
90
+
91
+ ## What this skill must NOT do
92
+
93
+ - **No token, no backend, no server call.** The only network action is the user's browser opening a public GitHub URL.
94
+ - **No local file fallback** (no `.jsonl`, no scratch capture as the "real" record). A scaffolded project is never meaningfully offline (the platform needs connectivity), and a local file never reaches the repo. The sole fallback for a failed auto-open is printing the URL.
95
+ - **Do not edit the local `.claude/` tree to "fix" the rule.** `bspecs sync` overwrites it; the fix must land in the repo via the issue.
96
+ - **Do not create `kind:*` labels** or pass them on `labels=` — only the `feedback` label exists; kind travels as text.
97
+ - **Do not file without the user's confirmation** (step 3).
@@ -9,7 +9,7 @@ A spec lives at `.claude/specs/<feature-name>/` and consists of three files: `re
9
9
 
10
10
  ## Prerequisite
11
11
 
12
- Read the `draft/README.md` of each component this feature will touch (see `CLAUDE.md` → "Reading module context — scoped to the task"). Read only the relevant components' READMEs, not the whole workspace. Those READMEs are your baseline for what each component does today — design decisions in Phase 2 should reference that knowledge, not re-derive it. If a relevant module's README is missing, ask the user to `/b6p-pull` it (or scaffold the README) before continuing.
12
+ Read the `draft/README.md` of each component this feature will touch (see `CLAUDE.md` → "Module context — read scoped to the task"). Read only the relevant components' READMEs, not the whole workspace. Those READMEs are your baseline for what each component does today — design decisions in Phase 2 should reference that knowledge, not re-derive it. If a relevant module's README is missing, ask the user to `/b6p-pull` it (or scaffold the README) before continuing.
13
13
 
14
14
  ## Steps
15
15
 
@@ -13,7 +13,7 @@ This workspace is a **local copy** of components that live on the BlueStep platf
13
13
  - A project may span **one or more Unit folders** (`U######/`). Unit folders are created by `b6p pull` — never by hand.
14
14
  - Each Unit folder contains one or more **components** of any type (MergeReport, Endpoint, Formula, OnDemand, Scheduled, Post-Save). A single project mixes types freely.
15
15
  - New B6P **components** are created on the platform, never locally. Inside an existing component, creating new `.ts` files locally is fine.
16
- - Sync with the platform via `b6p pull "<DAV URL>"` (first pull or re-sync) and `b6p push --file "<component-path>/..."` (deploy). See the Sync workflow section below for the exact invocation.
16
+ - Sync with the platform via the `/b6p-pull`, `/b6p-push`, and `/b6p-audit` skills (see the Skill quick reference).
17
17
 
18
18
  ## Critical rules (always apply)
19
19
 
@@ -44,43 +44,18 @@ This workspace is a **local copy** of components that live on the BlueStep platf
44
44
 
45
45
  Component type is encoded in `draft/info/metadata.json` (and the matching `config.json`). When you need to know whether a component is an Endpoint, MergeReport, etc., read those — don't infer from folder names.
46
46
 
47
- **Two distinct docs, by lifecycle:**
48
-
49
- - `<Component>/draft/README.md` — describes what the module **does today**. Lives inside `draft/` so it ships to the platform on push; anyone who pulls the module gets the doc. Scaffolded automatically by `/b6p-pull` when missing; if you change documented behavior, update the README in the same change.
50
- - `.claude/specs/<feature>/` — describes what you are about to **change or add** (requirements → design → tasks). Per feature, not per component. Created by `/spec-create`.
51
-
52
47
  ## The `B` object (platform API)
53
48
 
54
- | Namespace | Purpose |
55
- |---------------|-----------------------------------------------|
56
- | `B.net` | Outbound HTTP (`B.net.fetch()`) |
57
- | `B.util` | Utilities (logging, parsing, formatting) |
58
- | `B.io` | I/O, WebSocket push (`B.io.sendMessage()`) |
59
- | `B.db` | Database access |
60
- | `B.time` | Date/time — use this instead of native `Date` |
61
- | `B.queries` | Query objects defined on the platform |
62
- | `B.exports` | Cross-formula data sharing |
63
- | `B.user` | Current user (null in scheduled/cron scripts) |
64
- | `B.commit()` | Force a mid-script transaction commit |
65
-
66
- Full API patterns: read `.claude/instructions/bsjs-development.md` when writing or reviewing BsJs (load on demand — it is not auto-imported).
67
-
68
- ## Reading module context — scoped to the task
69
-
70
- **Read `draft/README.md` only for the component(s) the current task touches** — not every component in the workspace. A workspace can hold dozens of components totalling thousands of lines of README; loading all of them into every session is wasteful and slow.
71
-
72
- When a task names (or clearly implies) one or more components, read those READMEs before suggesting or implementing anything. Each module's `draft/README.md` describes what the module does today; it was scaffolded by `/b6p-pull` from the code, so treat it as the source of truth for current behavior. If you're unsure which component a request maps to, ask — don't pre-load everything to be safe.
73
-
74
- To discover which components exist without reading their READMEs, list the `U######/*/` directories or read a single `draft/info/metadata.json`.
49
+ All platform interaction goes through the global `B` object (`B.net`, `B.io`, `B.db`, `B.queries`, `B.exports`, `B.util`, …). Two inline gotchas worth never missing: use **`B.time`** for date/time, never native `Date`; **`B.user` is null** in scheduled/cron scripts. For the full namespace table and API patterns, read `.claude/instructions/bsjs-development.md` on demand (not auto-imported).
75
50
 
76
- If the component a task touches has no `draft/README.md` (or it's empty boilerplate), ask the user to run `/b6p-pull <DAV URL>` against that component to scaffold one. Don't proceed to edit code in a module whose README is missing.
51
+ ## Module context read scoped to the task
77
52
 
78
- ## Documentation and planning workflow
53
+ **Read `draft/README.md` only for the component(s) the current task touches** — not every component in the workspace. A workspace can hold dozens of components totalling thousands of lines of README; loading all of them into every session is wasteful and slow. If a request is ambiguous about which component it maps to, ask — don't pre-load everything to be safe. To discover which components exist without reading their READMEs, list the `U######/*/` directories or read a single `draft/info/metadata.json`.
79
54
 
80
- Two distinct artifacts, separated by lifecycle:
55
+ Two distinct docs, separated by lifecycle:
81
56
 
82
- - **`<Component>/draft/README.md`** — what the module **does today**. Read it for the component(s) a task touches (see "Reading module context" above). Update only when documented behavior changes. Ships to the platform on push, so other devs who pull the module get the doc.
83
- - **`.claude/specs/<feature>/`** — what we're about to **change or add** (requirements → design → tasks). Per feature, not per component. Created by `/spec-create`. Archived/deleted when the feature is done.
57
+ - **`<Component>/draft/README.md`** — what the module **does today**. Lives inside `draft/`, so it ships to the platform on push and anyone who pulls the module gets it. Scaffolded by `/b6p-pull` from the code (the source of truth for current behavior); if a missing or empty README blocks a task, ask the user to run `/b6p-pull <DAV URL>` rather than editing code blind. Update it in the same change whenever documented behavior changes.
58
+ - **`.claude/specs/<feature>/`** — what you're about to **change or add** (requirements → design → tasks). Per feature, not per component. Created by `/spec-create`; archived/deleted when the feature is done.
84
59
 
85
60
  ## Skill quick reference
86
61
 
@@ -117,24 +92,17 @@ For sync, status, and audit operations, infer naturally from what the user asks
117
92
 
118
93
  You don't need explicit confirmation to invoke these — they're either read-only or already have their own internal confirmation steps.
119
94
 
120
- ## Sync workflow (b6p CLI internals)
95
+ ## Sync workflow (b6p CLI)
121
96
 
122
- The skills above wrap these commands. You can run them directly if you need to, but prefer the skills for guidance and consistency.
97
+ The `/b6p-pull`, `/b6p-push`, and `/b6p-audit` skills own the exact `npx b6p …` invocations and their guidance prefer them over running the CLI by hand. `b6p` ships as a devDependency (`@bluestep-systems/b6p-cli`) and is invoked as `npx b6p` (resolves `node_modules/.bin/b6p` cross-platform — no global install); run `npm install` once if `node_modules` is missing.
123
98
 
124
- `b6p` ships as a devDependency of this project (`@bluestep-systems/b6p-cli`). Invoke it with `npx b6p`, which resolves `node_modules/.bin/b6p` cross-platform no global install, no shell or PATH detection. Run `npm install` once if `node_modules` is missing.
125
-
126
- **One-time platform auth (required before any pull/push/audit).** `b6p` needs BlueStep platform credentials, stored globally in `~/.b6p/` (once per machine — not per project). On a machine that has never run it, the **first** `b6p` command prompts for credentials interactively, which hangs Claude (`--yes` does not cover the credentials prompt). Before the first sync command, verify credentials exist with `test -f ~/.b6p/secrets.enc`; if absent, run `npx b6p auth set` once, then retry. The `/b6p-*` skills do this preflight automatically.
127
-
128
- - **Pull (DAV URL required):** `npx b6p --yes pull "<DAV URL>"`. The DAV URL is copied from the component's page in the BlueStep platform UI — `b6p pull` does not accept display names. First pull creates the `U######/` folder and the component skeleton.
129
- - **Push (file-driven):** `npx b6p --yes push --file "U######/<Component>/draft/scripts/app.ts"`. `--file` lets the CLI derive the destination from local metadata.
130
- - **Audit (read-only):** `npx b6p --yes --json audit --file "U######/<Component>/draft/scripts/app.ts"`. Lists files that differ between local and platform.
131
- - Always pass `--yes` so the CLI does not show interactive prompts that Claude cannot answer.
132
- - Both pull and push verify per-file integrity and only transfer files whose content changed.
133
- - If `npx b6p` cannot be resolved, run `npm install` in the project root (see the "Install dependencies" section of the project's `README.md`). For other errors, the VS Code b6p extension is an equivalent fallback.
99
+ **One-time platform auth:** `b6p` needs BlueStep credentials stored in `~/.b6p/` (once per machine). The first command on a fresh machine prompts interactively and would hang Claude, so run `npx b6p auth set` once before the first sync. The `/b6p-*` skills preflight this automatically.
134
100
 
135
101
  ## Self-improvement
136
102
 
137
- If you discover a B6P rule or pattern that is not documented here:
103
+ When you discover something worth changing, route it by **what** it is.
104
+
105
+ **B6P domain knowledge** not documented here (a platform rule, a BsJs pattern) — capture it **locally**:
138
106
 
139
107
  1. Apply it for the current task.
140
108
  2. Capture the rule in the right place:
@@ -143,6 +111,11 @@ If you discover a B6P rule or pattern that is not documented here:
143
111
  - A single-topic detail or sharp edge → a new or existing atomic file under `.claude/instructions/{reference,conventions,gotchas}/`, and add (or update) its one-line entry in `.claude/instructions/index.md` so it's discoverable.
144
112
  3. Note in your response: "Added rule: <description>".
145
113
 
114
+ **A bspecs tooling problem, or a rule that should reach _every_ project** — file it upstream with `/bspecs-feedback`. This `.claude/` tree is a scaffolded copy that `bspecs sync` overwrites, so a local edit here does not reach the canonical bspecs repo and does not survive the next sync. Use `/bspecs-feedback` when:
115
+
116
+ - a bspecs **tooling artifact** is wrong, broken, or missing — a skill, hook, subagent, instruction _template_, settings rule, or a `CLAUDE.md` rule that came from bspecs; or
117
+ - a B6P rule you found is general enough to belong in **every** scaffolded project (bspecs' shipped instruction templates), not just this one.
118
+
146
119
  ## Deep reference
147
120
 
148
121
  The deep platform and BsJs reference lives under `.claude/instructions/`. None of it is auto-imported — read on demand, only when the task needs it, so it doesn't sit in context every session.
@@ -154,6 +127,4 @@ The two on-demand overviews come first for high-frequency material:
154
127
  - `.claude/instructions/bsjs-development.md` — BsJs/TypeScript patterns, the `B` API, TS narrowing pitfalls. Read when writing or reviewing component code.
155
128
  - `.claude/instructions/b6p-platform.md` — platform architecture and concepts (Relate/Connect/Manage, component types, sync model, declarations). Read when a task hinges on platform behavior.
156
129
 
157
- Reach past the overviews into the indexed atomic files when you need a specific topic the overview only summarizes.
158
-
159
- The critical rules above (declarations, `.writable()`, `tsc`, `npx b6p`, per-component imports) are the must-always-apply subset — you don't need the deep files to honor them.
130
+ Reach past the overviews into the indexed atomic files when you need a specific topic the overview only summarizes. The Critical rules above are the must-always-apply subset — honor them without needing the deep files.