@harperfast/template-react-ts-studio 1.12.2 → 1.12.3

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 (20) hide show
  1. package/.agents/skills/harper-best-practices/AGENTS.md +632 -474
  2. package/.agents/skills/harper-best-practices/rules/automatic-apis.md +131 -55
  3. package/.agents/skills/harper-best-practices/rules/checking-authentication.md +155 -61
  4. package/.agents/skills/harper-best-practices/rules/deploying-to-harper-fabric.md +63 -85
  5. package/.agents/skills/harper-best-practices/rules/programmatic-table-requests.md +111 -52
  6. package/.agents/skills/harper-best-practices/rules/querying-rest-apis.md +38 -70
  7. package/.agents/skills/harper-best-practices/rules/schema-design-tooling.md +81 -69
  8. package/.agents/skills/harper-best-practices/rules/v5-upgrade.md +82 -96
  9. package/.agents/skills/harper-best-practices/rules.manifest.yaml +212 -0
  10. package/agent/skills/harper-best-practices/AGENTS.md +632 -474
  11. package/agent/skills/harper-best-practices/rules/automatic-apis.md +131 -55
  12. package/agent/skills/harper-best-practices/rules/checking-authentication.md +155 -61
  13. package/agent/skills/harper-best-practices/rules/deploying-to-harper-fabric.md +63 -85
  14. package/agent/skills/harper-best-practices/rules/programmatic-table-requests.md +111 -52
  15. package/agent/skills/harper-best-practices/rules/querying-rest-apis.md +38 -70
  16. package/agent/skills/harper-best-practices/rules/schema-design-tooling.md +81 -69
  17. package/agent/skills/harper-best-practices/rules/v5-upgrade.md +82 -96
  18. package/agent/skills/harper-best-practices/rules.manifest.yaml +212 -0
  19. package/package.json +1 -1
  20. package/skills-lock.json +1 -1
@@ -7,17 +7,17 @@ metadata:
7
7
  mode: generate
8
8
  sources:
9
9
  - release-notes/v5-lincoln/v5-migration.md
10
- sourceCommit: aa74b1cca7f4a511877730268f2fb6e628b50cf9
10
+ sourceCommit: 677ad213d67822e109c83619e181ca23a59823db
11
11
  inputHash: 01c0fafb9afa68a0
12
12
  ---
13
13
 
14
14
  # v5 Upgrade: Breaking Changes and Migration Guide
15
15
 
16
- Instructions for the agent to apply all required breaking-change fixes and recommended updates when migrating a Harper application to v5.
16
+ Instructions for the agent to apply when migrating a Harper application to v5, covering all breaking changes and required code updates.
17
17
 
18
18
  ## When to Use
19
19
 
20
- Apply this rule whenever upgrading an existing Harper application to v5, encountering v5 runtime errors related to module imports, `Table.get` return values, transaction context, process spawning, or VM module loading. Also apply when configuring `harperdb-config.yaml` for a v5 deployment.
20
+ Apply this rule when upgrading an existing Harper application to v5, when encountering runtime errors related to renamed packages, changed APIs, or security restrictions after a v5 upgrade, or when scaffolding new v5-compatible application code.
21
21
 
22
22
  ## How It Works
23
23
 
@@ -27,22 +27,20 @@ Apply this rule whenever upgrading an existing Harper application to v5, encount
27
27
  import { tables } from 'harper';
28
28
  ```
29
29
 
30
- 2. **Enable `allowInstallScripts` if packages require install scripts**: Harper v5 uses `--ignore-scripts` by default when installing packages. If your application requires installation scripts (e.g., to install additional binaries), set the `allowInstallScripts` option when deploying.
30
+ 2. **Enable `allowInstallScripts` if packages require install scripts**: Harper v5 uses `--ignore-scripts` by default when installing packages. If a package requires execution of install scripts (e.g., to install native binaries), set the `allowInstallScripts` option when deploying.
31
31
 
32
- 3. **Update `Table.get` usage — return value is now a plain frozen record object**: `Table.get` no longer returns an instance of the table class. The returned object is frozen and does not have table instance methods.
33
- - Replace `wasLoadedFromSource()` with `target.loadedFromSource`:
32
+ 3. **Update `Table.get` usage — return value is now a frozen record object**: `Table.get` now returns a plain record object, not a table class instance. The record is frozen; you cannot add or mutate properties directly.
33
+ - Replace direct property mutation:
34
34
 
35
35
  ```javascript
36
- // OLD — no longer works
37
- const record = await Table.get(id);
38
- if (record.wasLoadedFromSource()) {
39
- // record was loaded from origin (not cache)
40
- }
36
+ let record = await Table.get(id);
37
+ record = { ...record, property: 'changed' };
41
38
  ```
42
39
 
40
+ - Replace `wasLoadedFromSource()` with `loadedFromSource` on the `target` object:
41
+
43
42
  ```javascript
44
- // NEW
45
- const target = new RequestTarget(); // note that this is passed in if you are overriding the `get` method
43
+ const target = new RequestTarget();
46
44
  target.id = id;
47
45
  const record = await Table.get(target);
48
46
  if (target.loadedFromSource) {
@@ -50,107 +48,86 @@ Apply this rule whenever upgrading an existing Harper application to v5, encount
50
48
  }
51
49
  ```
52
50
 
53
- - The record object still has `getUpdatedTime` and `getExpiresAt` methods available.
51
+ The record objects still expose `getUpdatedTime` and `getExpiresAt` methods.
54
52
 
55
- - **Handle frozen records**: The record object is frozen — you cannot add or remove properties directly. Copy and spread instead:
53
+ 4. **Update transaction and context handling using `getContext`**: Harper v5 uses asynchronous context tracking. Context and the current transaction are automatically carried to all downstream calls — you no longer pass context explicitly. Import `getContext` and `transaction` from `harper`:
56
54
 
57
- ```javascript
58
- // OLD — throws in v5
59
- const record = await Table.get(id);
60
- record.property = 'changed';
61
- ```
55
+ ```javascript
56
+ import { getContext, transaction } from 'harper';
57
+ ```
62
58
 
63
- ```javascript
64
- // NEW
65
- let record = await Table.get(id);
66
- record = { ...record, property: 'changed' };
67
- ```
59
+ If your code previously omitted context to escape a transaction (e.g., to poll for updated data), explicitly commit the transaction and/or wrap each read in a new `transaction()` call:
68
60
 
69
- 4. **Update transaction and context handling**: Harper v5 uses asynchronous context tracking. Context and the current transaction are automatically carried to all calls, including `Table.get`. Code that previously omitted context to escape a transaction must now explicitly commit or start a new transaction.
70
- - Import `getContext` and `transaction` from `harper`:
61
+ ```javascript
62
+ import { setTimeout as delay } from 'node:timers/promises';
63
+ import { getContext, transaction } from 'harper';
64
+ class MyResource {
65
+ static async get(target) {
66
+ await getContext().transaction.commit();
67
+ while ((await transaction(() => Table.get(target))).status !== 'ready') {
68
+ await delay(100);
69
+ }
70
+ return Table.get(target);
71
+ }
72
+ }
73
+ ```
71
74
 
72
- ```javascript
73
- import { getContext, transaction } from 'harper';
74
- ```
75
+ 5. **Register allowed spawn commands via `allowedSpawnCommands`**: `spawn` and `execFile` may only launch executables listed in `applications.allowedSpawnCommands` in `harperdb-config.yaml`. Only the first token of the command is matched. `exec` is not usable through the substituted module; `execSync` always throws.
75
76
 
76
- - Explicitly commit the current transaction before reading updated data:
77
+ ```yaml
78
+ applications:
79
+ allowedSpawnCommands:
80
+ - npm
81
+ - node
82
+ ```
77
83
 
78
- ```javascript
79
- import { setTimeout as delay } from 'node:timers/promises';
80
- import { getContext, transaction } from 'harper';
81
- class MyResource {
82
- static async get(target) {
83
- await getContext().transaction.commit();
84
- while ((await transaction(() => Table.get(target))).status !== 'ready') {
85
- await delay(100);
86
- }
87
- return Table.get(target);
88
- }
89
- }
90
- ```
84
+ Additionally, `spawn`, `execFile`, and `fork` now require a `name` property in the `options` argument to prevent process multiplication across threads.
91
85
 
92
- 5. **Register allowed spawn commands via `allowedSpawnCommands`**: Any `spawn` or `execFile` call may only launch executables listed in `applications.allowedSpawnCommands` in `harperdb-config.yaml`. The `exec` function is not usable through the substituted module, and `execSync` always throws. The `spawn`, `execFile`, and `fork` functions also require a `name` property in the `options` argument to prevent process multiplication across threads.
86
+ 6. **Use `saveBeforeCommit` instead of `blob.save()`**: The `blob.save()` method has been removed. Pass the `saveBeforeCommit` flag in the options to the `Blob` constructor instead.
93
87
 
94
- 6. **Replace `blob.save()` with `saveBeforeCommit`**: The `blob.save()` method has been removed. Use the `saveBeforeCommit` flag in the options passed to the `Blob` constructor instead.
88
+ 7. **Handle `headers` on returned response objects**: If you return an object from a REST method with a `headers` property, Harper v5 will use it as the response headers.
95
89
 
96
- 7. **Configure the `moduleLoader` and `lockdown` settings**: Harper v5 loads application modules through Node.js's VM module API. Control all behavior in the `applications` section of `harperdb-config.yaml`:
90
+ 8. **Configure the VM module loader and `lockdown` in `harperdb-config.yaml`**: v5 loads application modules through Node.js's VM module API. Control all behavior under the `applications` key:
97
91
 
98
92
  ```yaml
99
93
  applications:
100
- lockdown: freeze-after-load # default; see below
101
- moduleLoader: vm-current-context # vm-current-context (default) | vm | native | compartment
102
- dependencyLoader: auto # auto (default) | app | native
103
- allowedDirectory: app # app (default) | any
104
- allowedSpawnCommands: # see "Spawning new processes" above
94
+ lockdown: freeze-after-load
95
+ moduleLoader: vm-current-context
96
+ dependencyLoader: auto
97
+ allowedDirectory: app
98
+ allowedSpawnCommands:
105
99
  - npm
106
100
  - node
107
- # allowedBuiltinModules: [] # if omitted, all Node.js built-ins are allowed
108
101
  ```
109
102
 
110
103
  **`moduleLoader` options:**
111
104
 
112
105
  | Value | Behavior |
113
106
  | -------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
114
- | `vm-current-context` | Default. VM loader in Harper's own context; shares intrinsics with Harper for best compatibility. |
115
- | `vm` | VM loader in a separate per-application context with its own intrinsics; stronger isolation but can cause `instanceof` failures. |
116
- | `native` | Standard Node.js `import()` with no VM loader; application-specific context (tagged logging, per-app `config`) not available. |
117
- | `compartment` | SES Compartment-based loading; for specialized sandboxing only. |
107
+ | `vm-current-context` | Default. VM loader in Harper's own context; shares intrinsics with Harper. Best compatibility. |
108
+ | `vm` | VM loader in a separate per-application context with its own intrinsics. Stronger isolation but may cause `instanceof` failures. |
109
+ | `native` | Standard Node.js `import()`. No VM loader; application-specific context (`logger`, `config`) unavailable. |
110
+ | `compartment` | SES Compartment-based loading. For specialized sandboxing only. |
118
111
 
119
112
  **`lockdown` options:**
120
113
 
121
- | Value | Behavior |
122
- | ------------------- | ------------------------------------------------------------- |
123
- | `freeze-after-load` | Default. Freezes intrinsics after all components have loaded. |
124
- | `freeze` | Freezes intrinsics before loading any application code. |
125
- | `ses` | Full SES lockdown via the `ses` package; strictest. |
126
- | `none` | No lockdown. Use as a temporary workaround only. |
127
- - To disable the VM loader entirely and restore pre-v5 behavior:
128
-
129
- ```yaml
130
- applications:
131
- moduleLoader: native
132
- ```
133
-
134
- - To restrict which Node.js built-ins are accessible:
114
+ | Value | Behavior |
115
+ | ------------------- | ------------------------------------------------------- |
116
+ | `freeze-after-load` | Default. Freezes intrinsics after all components load. |
117
+ | `freeze` | Freezes intrinsics before loading any application code. |
118
+ | `ses` | Full SES lockdown via the `ses` package. Strictest. |
119
+ | `none` | No lockdown. Use as a temporary workaround only. |
135
120
 
136
- ```yaml
137
- applications:
138
- allowedBuiltinModules:
139
- - fs
140
- - path
141
- - http
142
- ```
121
+ To disable the VM loader entirely and restore pre-v5 behavior:
143
122
 
144
- - To allow loading modules from outside the application directory:
145
-
146
- ```yaml
147
- applications:
148
- allowedDirectory: any
149
- ```
123
+ ```yaml
124
+ applications:
125
+ moduleLoader: native
126
+ ```
150
127
 
151
128
  ## Examples
152
129
 
153
- **Full transaction context migration:**
130
+ **Full transaction polling pattern (v5):**
154
131
 
155
132
  ```javascript
156
133
  import { setTimeout as delay } from 'node:timers/promises';
@@ -158,9 +135,7 @@ import { getContext, transaction } from 'harper';
158
135
 
159
136
  class MyResource {
160
137
  static async get(target) {
161
- // Explicitly commit the transaction to see updated data
162
138
  await getContext().transaction.commit();
163
- // Start a new transaction for each get to see the latest data
164
139
  while ((await transaction(() => Table.get(target))).status !== 'ready') {
165
140
  await delay(100);
166
141
  }
@@ -169,11 +144,9 @@ class MyResource {
169
144
  }
170
145
  ```
171
146
 
172
- **`Table.get` with `loadedFromSource` check:**
147
+ **Checking `loadedFromSource` after `Table.get`:**
173
148
 
174
149
  ```javascript
175
- import { tables } from 'harper';
176
-
177
150
  const target = new RequestTarget();
178
151
  target.id = id;
179
152
  const record = await Table.get(target);
@@ -195,11 +168,24 @@ applications:
195
168
  - node
196
169
  ```
197
170
 
171
+ **Restricting allowed built-in modules:**
172
+
173
+ ```yaml
174
+ applications:
175
+ allowedBuiltinModules:
176
+ - fs
177
+ - path
178
+ - http
179
+ ```
180
+
198
181
  ## Notes
199
182
 
200
- - Use `getContext` (imported from `harper`) to access the current transaction anywhere in application code without passing context explicitly through every call.
201
- - Harper functions and APIs should be accessed through the `harper` package rather than through global variables.
202
- - Use `static` methods on Resources/Tables to implement endpoints, and access request information from the request `target` argument or via `getContext`.
203
- - `lockdown: none` is only a temporary workaround for dependencies that mutate intrinsic prototypes at runtime; do not leave it set in production.
204
- - Under `lockdown: ses`, the constrained (https-only) `fetch` is applied only in `vm` mode. In `vm-current-context` and `native` modes, application code uses the standard global `fetch`.
205
- - In production, `allowedDirectory: app` is the default; dev mode installs default to `allowedDirectory: any`.
183
+ - Always import Harper APIs from `'harper'`, not from global variables or `'harperdb'`.
184
+ - `getContext` is exported from `'harper'` and provides access to the current transaction without passing context explicitly.
185
+ - Record objects returned by `Table.get` are frozen — spread into a new object before modifying.
186
+ - `loadedFromSource` is a property on the `target` object, replacing the removed `wasLoadedFromSource()` instance method.
187
+ - `saveBeforeCommit` replaces the removed `blob.save()` method.
188
+ - The `headers` property on a returned REST response object is used as response headers.
189
+ - Under `lockdown: ses`, the constrained `fetch` applies only in `vm` mode. In `vm-current-context` and `native` modes, application code uses the standard global `fetch`.
190
+ - In production, `allowedDirectory: app` is the default; modules outside the application directory tree will throw. Set `allowedDirectory: any` only if legitimately required.
191
+ - `dependencyLoader: native` is a narrower option than `moduleLoader: native` — it uses native loading only for npm packages while keeping the VM loader for first-party application source files.
@@ -149,14 +149,61 @@ rules:
149
149
  order: 1
150
150
  mode: generate
151
151
  sources:
152
+ # overview.md is sourced section-by-section rather than whole-file: at
153
+ # ~15KB it was the largest single input in the skill, and the compression
154
+ # dropped conditional requests, content negotiation, and the `GET`
155
+ # caveats outright. Listing sections narrows the input and makes a docs
156
+ # rename fail loudly in sliceSection. The dropped headings are the
157
+ # `# REST Overview` preamble (marketing + a version badge) and
158
+ # `## See Also` (link list); `## OpenAPI` is kept because the rule
159
+ # documents `GET /openapi`.
152
160
  - path: reference/v5/rest/overview.md
161
+ section: 'How the REST Interface Works'
153
162
  role: primary
163
+ - path: reference/v5/rest/overview.md
164
+ section: 'Configuration'
165
+ role: primary
166
+ - path: reference/v5/rest/overview.md
167
+ section: 'Tables and Their Automatic Endpoints'
168
+ role: primary
169
+ - path: reference/v5/rest/overview.md
170
+ section: 'URL Structure'
171
+ role: primary
172
+ - path: reference/v5/rest/overview.md
173
+ section: 'HTTP Methods'
174
+ role: primary
175
+ - path: reference/v5/rest/overview.md
176
+ section: 'Content Types'
177
+ role: primary
178
+ - path: reference/v5/rest/overview.md
179
+ section: 'OpenAPI'
180
+ role: supplemental
154
181
  - path: reference/v5/rest/websockets.md
155
182
  role: supplemental
156
183
  must_cover:
157
184
  - 'rest: true'
158
185
  - '@export'
159
186
  - 'WebSocket'
187
+ # Each of the following occurs exactly once across the sourced sections,
188
+ # so each pins one previously-dropped section:
189
+ # If-None-Match / 304 Not Modified → `#### Conditional Requests and Caching`
190
+ # `Accept` → `## Content Types` (bare `Accept`
191
+ # would be satisfied by the SSE `Accept: text/event-stream` mention
192
+ # under `## Tables and Their Automatic Endpoints`, which gates nothing)
193
+ # .msgpack → the reserved content-type selector
194
+ # suffixes and the undeclared-property caveat in `### GET`
195
+ - 'If-None-Match'
196
+ - '304 Not Modified'
197
+ - 'ETag'
198
+ - '`Accept`'
199
+ - '.msgpack'
200
+ # Pinned after the first regen dropped them while restoring the three
201
+ # sections above — an unanchored fact is only ever one regen from being
202
+ # compressed away, whichever fact it is:
203
+ # Location (2×) → the primary key POST returns in that header
204
+ # @updatedTime (1×) → the three exceptions PUT always applies
205
+ - 'Location'
206
+ - '@updatedTime'
160
207
  cross_links:
161
208
  - querying-rest-apis
162
209
  - real-time-apps
@@ -207,13 +254,123 @@ rules:
207
254
  - path: reference/v5/resources/resource-api.md
208
255
  section: 'Session and Login from a Resource'
209
256
  role: primary
257
+ # JWT half. Sectioned *and* anchored deliberately.
258
+ #
259
+ # The generator concatenates every resolved source into one unlabeled
260
+ # blob (lib/sources.mjs `resolveSources`) and the system prompt tells the
261
+ # model to "keep it tight" and omit tangential detail. So whatever no
262
+ # `must_cover` anchor requires is exactly what gets compressed away —
263
+ # anchor starvation, not source ordering or size. As a whole-file source
264
+ # with no JWT anchors, this half collapsed to a single "use JWT instead"
265
+ # pointer and lost the token issue/refresh flow entirely.
266
+ #
267
+ # `must_cover` is the actual gate: it is enforced verbatim
268
+ # (validate-generated.mjs, `body.includes(must)`) and is echoed to the
269
+ # model as a hard requirement. `section:` only narrows what gets fed in —
270
+ # it helps because it tends to come with anchors, not on its own. Prefer
271
+ # identifier-shaped anchors that occur in exactly one sourced section; an
272
+ # anchor matching many sections gates nothing in particular.
273
+ #
274
+ # `role:` is a human-facing annotation only — the generator never reads
275
+ # it (it is enum-checked by the validator and otherwise unused).
276
+ - path: reference/v5/security/jwt-authentication.md
277
+ section: 'Create Authentication Tokens'
278
+ role: primary
210
279
  - path: reference/v5/security/jwt-authentication.md
280
+ section: 'Using the Operation Token'
281
+ role: supplemental
282
+ - path: reference/v5/security/jwt-authentication.md
283
+ section: 'Refreshing the Operation Token'
284
+ role: primary
285
+ - path: reference/v5/security/jwt-authentication.md
286
+ section: 'Scoped Tokens (Inline Role)'
287
+ role: primary
288
+ - path: reference/v5/security/jwt-authentication.md
289
+ section: 'Issuing Tokens from a Custom Resource'
290
+ role: primary
291
+ - path: reference/v5/security/jwt-authentication.md
292
+ section: 'Token Expiry Configuration'
293
+ role: supplemental
294
+ # Sourced so the rule carries the *negative* recommendation too. Without
295
+ # it the generated body pushed JWT at every non-browser client, while the
296
+ # source actually points simple and server-to-server callers at Basic
297
+ # Authentication — i.e. the rule was inviting token lifecycle complexity
298
+ # the docs tell you to avoid.
299
+ - path: reference/v5/security/jwt-authentication.md
300
+ section: 'When to Use JWT Auth'
301
+ role: primary
302
+ - path: reference/v5/security/jwt-authentication.md
303
+ section: 'Security Notes'
211
304
  role: supplemental
212
305
  must_cover:
306
+ # Session half.
213
307
  - 'getCurrentUser()'
214
308
  - 'getContext()'
215
309
  - 'context.login'
216
310
  - 'enableSessions'
311
+ # JWT half. One anchor per sourced section wherever an identifier is
312
+ # unique to it: `server.operation` pins the custom-resource section,
313
+ # `add_role`/`expires_in` the scoped-token section, `HTTPS` the security
314
+ # notes (`operationTokenTimeout` alone no longer distinguishes those two,
315
+ # since it occurs in both). `create_authentication_tokens` is the one
316
+ # unavoidably broad anchor — it is the subject of its own section but is
317
+ # back-referenced by three others, so it gates presence, not depth.
318
+ - 'create_authentication_tokens'
319
+ - 'refresh_operation_token'
320
+ - 'server.operation'
321
+ - 'add_role'
322
+ - 'expires_in'
323
+ - 'operationTokenTimeout'
324
+ - 'HTTPS'
325
+ # Scoped tokens are rejected with a 401 by nodes too old to understand
326
+ # them. Unanchored, that bullet was dropped the first time this rule was
327
+ # regenerated after the section was added — a rollout hazard, so it gets
328
+ # its own anchor rather than relying on the section being sourced.
329
+ - 'mixed-version'
330
+ # Operation names alone gate vocabulary, not mechanics: a regeneration
331
+ # could keep `create_authentication_tokens`/`refresh_operation_token` and
332
+ # still drop the token exchange, the refresh timeout, or what the third
333
+ # `server.operation` argument does. The anchors below pin those specific
334
+ # semantics. Each occurs exactly once in the doc and in exactly one
335
+ # sourced section, so it forces depth in that section rather than a
336
+ # passing mention anywhere.
337
+ #
338
+ # Two caveats this list is deliberately built around:
339
+ #
340
+ # 1. `must_cover` is a substring test (`body.includes(must)`), so an
341
+ # anchor contained in another anchor gates nothing extra.
342
+ # `operation_token` is already implied by `refresh_operation_token`,
343
+ # and `refresh_token` by `Bearer <refresh_token>` — listing either
344
+ # separately would be noise, not coverage.
345
+ # 2. A bare-fragment anchor invites literal satisfaction. Anchoring the
346
+ # exchange as `Bearer <refresh_token>` — rather than `refresh_token`,
347
+ # which occurs in three sourced sections — requires the body to show
348
+ # the refresh token actually being presented as the Bearer credential.
349
+ #
350
+ # `third argument` + `current authenticated user` pin the authorize
351
+ # behavior together. `authorize: true` is the doc's prose name for it but
352
+ # is rejected as an anchor: the code passes `true` positionally, so
353
+ # forcing that string risks a body an agent transcribes as an options
354
+ # object.
355
+ - 'Bearer <refresh_token>'
356
+ - 'refreshTokenTimeout'
357
+ - 'third argument'
358
+ - 'current authenticated user'
359
+ # When to Use JWT Auth. `Basic Authentication` is a superstring of the
360
+ # plain `Basic Auth` the section also contains — which is the better
361
+ # anchor, since `Basic Auth` additionally appears in a code comment in
362
+ # the custom-resource section and could be satisfied from there while the
363
+ # recommendation itself stayed compressed away. `server-to-server` pins
364
+ # the scenario the source actually steers away from JWT.
365
+ #
366
+ # `preferred over Basic Auth` carries the source's *conditional* framing.
367
+ # It is anchored because sourcing the section alone was not enough: the
368
+ # body kept a flat "for non-browser clients, use JWT" directive and filed
369
+ # the Basic Auth alternative under Notes, which reads as a contradiction.
370
+ # Anchoring the conditional forces the choice to be stated as a tradeoff.
371
+ - 'Basic Authentication'
372
+ - 'server-to-server'
373
+ - 'preferred over Basic Auth'
217
374
  cross_links:
218
375
  - custom-resources
219
376
 
@@ -293,6 +450,36 @@ rules:
293
450
  - 'search('
294
451
  - 'addTo'
295
452
  - 'select'
453
+ # Query Object options that were covered before the transaction material
454
+ # was pinned, and that the regen then compressed out to make room. They
455
+ # are anchored now rather than left to survive on luck; each occurs only
456
+ # in § Query Object. `sort` also carries the `@indexed`-or-`conditions`
457
+ # requirement and `allowFullScan`, which have no meaning without it.
458
+ - 'sort'
459
+ - '@indexed'
460
+ - 'explain'
461
+ - 'enforceExecutionOrder'
462
+ # The `transaction(context?, callback)` source was already declared with a
463
+ # `section:` and was still dropped in full — proof that sectioning narrows
464
+ # the input but does not hold content in. Only an anchor does. Each of
465
+ # these occurs in exactly one sourced section:
466
+ # transaction() (2×) → § `transaction(context?, callback)`
467
+ # resetReadSnapshot() (1×) → its `### Transaction Object` member table
468
+ # atomicity (1×) → `### Transaction Scope and Atomicity`
469
+ # limit / offset (1× each) → § Query Object, the pagination pair
470
+ # Balanced parens deliberately: anchoring the bare `transaction(` also
471
+ # gates the section, but the model satisfies it literally and writes
472
+ # prose like "wrap background work in `transaction(`". `transaction()`
473
+ # contains `transaction(`, so it is the stricter anchor as well.
474
+ - 'transaction()'
475
+ - 'resetReadSnapshot()'
476
+ - 'atomicity'
477
+ - 'limit'
478
+ - 'offset'
479
+ # § Module Loading's one piece of actionable build guidance: keep `harper`
480
+ # external when bundling for SSR, or it is bundled instead of resolving
481
+ # to the runtime. Only occurrence of `vite.config` in the sources.
482
+ - 'vite.config'
296
483
 
297
484
  - rule: typescript-type-stripping
298
485
  description: How to run TypeScript files directly in Harper without a build step.
@@ -347,6 +534,28 @@ rules:
347
534
  must_cover:
348
535
  - 'harper deploy'
349
536
  - 'harper login'
537
+ # `### Provisioning a Deploy Credential` is already inside the sourced
538
+ # `## Remote Management` slice, and its security guidance was compressed
539
+ # away anyway. These anchors pin it; each occurs only in that slice:
540
+ # fine-grained (2×) → prefer a fine-grained PAT over the `gh` CLI token
541
+ # read:org (1×) → the account-wide scopes the `gh` token carries
542
+ # enc:v1: (1×) → the client-side-encryption envelope
543
+ # They double as the rename detector for that subheading: if it is
544
+ # retitled or removed, the content goes and these anchors fail.
545
+ - 'fine-grained'
546
+ - 'Contents: Read-only'
547
+ - 'read:org'
548
+ - 'enc:v1:'
549
+ # Pinned after the first regen dropped it while restoring the credential
550
+ # guidance: `refs/heads/*` and `refs/tags/*` (or a bare branch/tag) are
551
+ # the only refs a `by_ref` deploy can fetch.
552
+ - 'refs/tags'
553
+ # Also pinned after a regen dropped it: `setup=true` calls
554
+ # `get_secrets_public_key` / `set_secret` / `grant_secret`, so it must be
555
+ # run with an administrative credential, not the CI identity it
556
+ # provisions for. The source escapes it as `super\_user`; every generated
557
+ # body so far unescapes, so the anchor is the unescaped form.
558
+ - 'super_user'
350
559
  cross_links:
351
560
  - creating-a-fabric-account-and-cluster
352
561
 
@@ -422,6 +631,9 @@ rules:
422
631
  - 'moduleLoader'
423
632
  - 'lockdown'
424
633
  - 'Table.get'
634
+ # `## Response Objects` had no anchor and was dropped in full. Every
635
+ # occurrence of `headers` in the whole-file source is in that section.
636
+ - 'headers'
425
637
 
426
638
  - rule: delegating-to-the-built-in-agent
427
639
  description: How to delegate tasks to Harper's built-in agent via the CLI and the agent operations API.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@harperfast/template-react-ts-studio",
3
- "version": "1.12.2",
3
+ "version": "1.12.3",
4
4
  "type": "module",
5
5
  "repository": "github:HarperFast/create-harper",
6
6
  "scripts": {},
package/skills-lock.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "source": "harperfast/skills",
6
6
  "sourceType": "github",
7
7
  "skillPath": "harper-best-practices/SKILL.md",
8
- "computedHash": "827d6bddb39ad1cfdf2ea60327aa73c735fd1ab2f6c5d0191db737c63e98c186"
8
+ "computedHash": "ab02386c2af20dd8f3b782bb9a884dafd8a0cd9ce313c715bbabbf83453d4e74"
9
9
  },
10
10
  "harper-mcp": {
11
11
  "source": "harperfast/skills",