@orkestrel/scaffold 0.0.88 → 0.0.89

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.
@@ -25,7 +25,7 @@ description: Run an Orkestrel release from layer order to registry confirmation.
25
25
  | `scripts/compare.ts` | `[--package NAME] [--version V] [--tarball PATH] [--dist dist] [--json]`, run from the package root after its build: fetches the published tarball and lists the material differences from the rebuilt `dist/` (sourcemaps excluded, whitespace-only differences ignored). Exit 0 when nothing differs, 3 when something does, 2 when the tarball cannot be read, 64 on usage (no package name, no `dist/`, or `--tarball` with no path). |
26
26
  | `scripts/pins.ts` | `--version PRIOR [--range PRIOR_RANGE ...] [--paths src,tests] [--json]`, run from the package root: lists every line under the paths carrying the prior version literal or a prior range literal, once per line, sweeping a one-comparator range such as `^8.3.0` for its bare version in any form. Exit 0 with no hits, 3 with hits, 64 on usage (no literal, or a flag with no value). |
27
27
  | `scripts/wave.ts` | `--visit [--target DIR] [--offline] [--from STEP] [--to STEP] [--prior VERSION] [--dry-run] [--json] [--out FILE]` runs one repository's visit in the order [wave.md](references/wave.md) § Visit a repository fixes (pin, commit, overwrite, verify, install, pins, format, gates, compare), stops at the first failing step, and prints each step's exit (a failed step names the `code: message` refusal or the `note` a JSON verb writes on stdout, else the last stderr lines), the bump ruling (the rebuilt dist against the published tarball, and the final runtime dependency set against the published manifest), and a row for `.orkestrel/release.md`; `--prior` names the version a pre-ruled bump replaced so the self-pin sweep finds it; `--plan` prints the packages by layer from the catalog table. Exit 0 when every step passed, 1 when one failed, 2 when a reading failed, 64 on usage. |
28
- | `scripts/window.ts` | `--whoami [--wait SECONDS]` reads or polls `npm whoami`; `--login` prints the operator's login command and polls `whoami` until it answers; `--publish DIR... --otp CODE` uploads back to back with `npm publish --ignore-scripts --otp`, journals each upload under `tmp/units/`, stops at the first refusal naming where to resume, and confirms each accepted version against the registry; `--confirm NAME@VERSION...` re-reads the registry until it serves the version. Exit 0 on success, 3 when not. |
28
+ | `scripts/window.ts` | `--whoami [--wait SECONDS]` reads or polls `npm whoami`; `--login` prints the operator's login command and polls `whoami` until it answers; `--publish DIR... --otp CODE` refuses a batch whose nearest `.git` entry is a file, including ancestor entries, with exit 3 before contacting npm; it names a linked worktree's primary clone or identifies a submodule, then uploads back to back with `npm publish --ignore-scripts --otp`, journals each upload under `tmp/units/`, stops at the first refusal naming where to resume, and confirms each accepted version against the registry; `--confirm NAME@VERSION...` re-reads the registry until it serves the version. Exit 0 on success, 3 when not. |
29
29
 
30
30
  The user's current instruction wins. `references/release.md` owns the credential
31
31
  and authorization law; nothing here weakens it.
@@ -93,6 +93,8 @@ registry copy over any staged tip, so every consumer stays red until that tip is
93
93
 
94
94
  ## Prepare a layer
95
95
 
96
+ - Compare release packs between hosts by paths, sizes, and content hashes, and report executable-mode differences separately; a pack made on Windows records `0644` for an entry git tracks as `100755`.
97
+
96
98
  An unpublished package's first version is `0.0.1`. Do not bump it before that first publish. The
97
99
  registry has nothing to serve, so there is no version to move away from, and bumping produces a
98
100
  package whose history starts at a number nothing explains.
@@ -9,6 +9,8 @@ rest of the layer either fits inside it or takes another approval.
9
9
 
10
10
  ## Arm the terminal
11
11
 
12
+ - Before any upload, walk from each package directory to its nearest ancestor carrying a `.git` entry; refuse the batch with exit 3 before contacting npm when that entry is a file. For a linked worktree, resolve its `gitdir:` path and `commondir` file and publish from the named primary clone; for a submodule without `commondir`, publish from a standalone clone.
13
+
12
14
  - Read the session with `node .agents/skills/orkestrel-publish/scripts/window.ts --whoami`; `--login` prints the command the operator runs in a real terminal and polls `whoami` until it answers. No child of a script holds the TTY npm needs, so the login itself is the operator's.
13
15
 
14
16
  - Run the login and every browser-authorized publish under `script -qfc '<command>' <log>`. npm
@@ -6,7 +6,9 @@
6
6
  // --whoami reads `npm whoami` once, or polls it every 5 seconds until it answers or the wait ends.
7
7
  // --login prints the command the operator runs in a real terminal (npm offers its approval only to a
8
8
  // TTY, so no child of this script can hold it), then polls `npm whoami` until it answers or the wait
9
- // ends. --publish re-reads `npm whoami` immediately before the first upload and refuses when it
9
+ // ends. --publish refuses linked worktrees and submodules found at a package or its ancestors,
10
+ // and names a linked worktree's primary clone.
11
+ // It then re-reads `npm whoami` immediately before the first upload and refuses when it
10
12
  // answers nothing, then runs `npm publish --ignore-scripts --browser=false --otp=CODE` in each
11
13
  // directory back to back under captured pipes (npm's non-TTY guard turns a refused code into
12
14
  // EOTP with no prompt, so the path needs no TTY), journals each upload to tmp/units/publish-<name>.log, reads the `+ name@version`
@@ -14,8 +16,8 @@
14
16
  // each accepted version against the registry. --confirm re-reads the registry for each name@version every 5
15
17
  // seconds until it serves the version or the wait (default 120 seconds) ends. Exit 0 when the mode
16
18
  // succeeded, 3 when it did not, 64 on usage.
17
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
18
- import { join } from 'node:path'
19
+ import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs'
20
+ import { dirname, join, resolve } from 'node:path'
19
21
  import {
20
22
  readList,
21
23
  readMissingFlags,
@@ -39,8 +41,8 @@ interface Upload {
39
41
  }
40
42
 
41
43
  function waitFor(ms: number): Promise<void> {
42
- return new Promise((resolve) => {
43
- setTimeout(resolve, ms)
44
+ return new Promise((settle) => {
45
+ setTimeout(settle, ms)
44
46
  })
45
47
  }
46
48
 
@@ -196,6 +198,33 @@ async function main(argv: readonly string[]): Promise<number> {
196
198
  )
197
199
  return 64
198
200
  }
201
+ for (const directory of directories) {
202
+ let root = resolve(directory)
203
+ while (!existsSync(join(root, '.git')) && dirname(root) !== root) root = dirname(root)
204
+ const entry = join(root, '.git')
205
+ if (!existsSync(entry) || !statSync(entry).isFile()) continue
206
+ const line = readFileSync(entry, 'utf8')
207
+ .split(/\r\n|\n/u)
208
+ .find((value) => value.startsWith('gitdir:'))
209
+ const target = line?.slice('gitdir:'.length).trim()
210
+ if (target === undefined || target === '') {
211
+ console.error(
212
+ `window: ${directory} has a .git file without a gitdir: path; cannot identify its primary clone`,
213
+ )
214
+ return 3
215
+ }
216
+ const gitdir = resolve(root, target)
217
+ const common = join(gitdir, 'commondir')
218
+ if (!existsSync(common)) {
219
+ console.error(`window: ${directory} is a submodule; publish from a standalone clone`)
220
+ return 3
221
+ }
222
+ const primary = dirname(resolve(gitdir, readFileSync(common, 'utf8').trim()))
223
+ console.error(
224
+ `window: ${directory} is a linked worktree; publish from its primary clone at ${primary} so npm records gitHead`,
225
+ )
226
+ return 3
227
+ }
199
228
  // A stored credential expires mid-session, so the session-start answer does not hold here.
200
229
  if (readWhoami() === undefined) {
201
230
  console.error(
@@ -292,6 +292,8 @@ Use the real browser as the system under test:
292
292
 
293
293
  - Do not replace DOM events, storage, observers, viewports, layout methods, pointer, or drag APIs unless the browser genuinely lacks one.
294
294
  - Prefer real nodes, events, styles/layout, and observers.
295
+ - In a file compiled by a browser or Vue scope, never load `@vitest/browser-playwright` types; read `cdp()` as `unknown`, and narrow `send` and each reply with guards.
296
+ - When a browser test moves the real pointer through CDP, move it to `(-1, -1)` in the case's `finally` block.
295
297
  - Centralize event factories: `createPointerEvent`, `createDragEvent`, `typeInput`, `fireTransitionEnd`.
296
298
  - Centralize DOM builders: `createButtonElement`, `createDropdownElements`, `createModalElement`.
297
299
  - Assert DOM state, emitted events, callback records, focus, classes, attributes, and public API state.
@@ -242,7 +242,7 @@ then runs the configured scoped checks that prove environment isolation.
242
242
  | Scope | `lib` | `types` | Permitted host globals |
243
243
  | ---------------------------- | --------------------------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
244
244
  | `src:core`, `app:core` | `["ESNext","WebWorker"]` | `[]` | WHATWG web interop: fetch family, streams, URL, Abort, encoders, crypto, timers, console, DOMException, structuredClone; no DOM, no Node |
245
- | `src:browser`, `app:browser` | `["ESNext","DOM","DOM.Iterable"]` | default | DOM; no Node |
245
+ | `src:browser`, `app:browser` | `["ESNext","DOM","DOM.Iterable"]` | `["vite/client"]` | DOM; no Node |
246
246
  | `src:server`, `app:server` | `["ESNext"]` | `["node"]` | Node; no DOM |
247
247
  | `src:styles`, `src:<name>` | `["ESNext"]` | `["vite/client"]` | Vite SCSS module declaration only |
248
248
  | `src:vue` | `["ESNext","DOM","DOM.Iterable"]` | `["vite/client"]` | DOM; no Node |
@@ -2067,6 +2067,12 @@ read or write that file.
2067
2067
 
2068
2068
  ## Generated workspace
2069
2069
 
2070
+ The generated factories name unnamed browser instances after merging the caller's override, using
2071
+ the merged project label and browser. An override setting the label to `widgets` produces
2072
+ `widgets (chromium)`, including for an instance added by the override. Explicit instance names stay
2073
+ unchanged. Vue and journey factories discard inherited instance names before applying their own
2074
+ labels. Vitest reports the same names when a wrapper runs alone and when the root registers it.
2075
+
2070
2076
  A workspace's file set is a function of its axes, its structural facts, and its extensions.
2071
2077
  Nothing is fixed except the manifest.
2072
2078
 
@@ -2084,7 +2090,8 @@ Nothing is fixed except the manifest.
2084
2090
  `dist/`, and `npm run check` would wait on `npm run build`. Every subpath is written before the
2085
2091
  bare specifier, because `vite.config.ts` derives its `alias` record from these entries in order and
2086
2092
  a bare specifier also matches its own subpaths. An `app` environment publishes nothing and maps no
2087
- such entry.
2093
+ such entry. Browser and Vue scoped configurations exclude Node globals. Browser setup helpers
2094
+ read CDP sessions as `unknown` and guard the members they use.
2088
2095
  - One template artifact, `configs/browsers.ts`, for a workspace selecting `browser` on either
2089
2096
  environment axis or in its setup runtime list.
2090
2097
  It resolves the Chromium the Playwright provider launches, and the root `vite.config.ts` calls it
@@ -2110,7 +2117,8 @@ Nothing is fixed except the manifest.
2110
2117
  journey per application of a journey workspace. Surfaces and extensions lists each face.
2111
2118
  - One template artifact, `tests/distribution.test.ts`, for a workspace publishing any `src`
2112
2119
  environment or sheet face. It is the packed-package proof, and it is claimed by presence rather
2113
- than birth, so a workspace that replaces it keeps its replacement. A published browser environment
2120
+ than birth, so a workspace that replaces it keeps its replacement. The suite stages its consumer
2121
+ during setup; listing the project creates no distribution staging directory. A published browser environment
2114
2122
  adds the
2115
2123
  real-browser stage to it: the stage bundles the installed package with the workspace's own
2116
2124
  `configs/browsers.ts` resolution, serves the bundle over a loopback server, and drives it in
@@ -2609,7 +2617,9 @@ The generated guide index lists each occupied Vue face with its source, tests, a
2609
2617
  - [`tests/src/core/cloners.test.ts`](../tests/src/core/cloners.test.ts) — ownership of a snapshot
2610
2618
  taken from a hostile value.
2611
2619
  - [`tests/src/core/templates.test.ts`](../tests/src/core/templates.test.ts) — the frozen template
2612
- definitions.
2620
+ definitions and generated configurations under live Vitest. The isolated `templates` project runs
2621
+ through `test:templates` in `prepublishOnly`; its compiler and Chromium processes run apart from
2622
+ the `src:core` project.
2613
2623
  - [`tests/src/core/constants.test.ts`](../tests/src/core/constants.test.ts) — the seeded rows named
2614
2624
  as a set, the floor form every shared table and this manifest carry, and the emitted TypeScript
2615
2625
  bound.
@@ -244,7 +244,7 @@
244
244
  "storage": "agents/skills/orkestrel-harden/SKILL.md",
245
245
  "destination": ".agents/skills/orkestrel-harden/SKILL.md",
246
246
  "executable": false,
247
- "digest": "fdb32a356c23d911f47a417b999397a188b508278cbf19d92b21e72979fea4c2"
247
+ "digest": "8d108a0545b9f70e7c27477af988b990d6a8f772649f04ef21bae99e08e4fa05"
248
248
  },
249
249
  {
250
250
  "storage": "agents/skills/orkestrel-harden/agents/openai.yaml",
@@ -280,7 +280,7 @@
280
280
  "storage": "agents/skills/orkestrel-harden/scripts/discovery.ts",
281
281
  "destination": ".agents/skills/orkestrel-harden/scripts/discovery.ts",
282
282
  "executable": false,
283
- "digest": "06128d912f79de527522075e7ebb3ccf24be864fad1015f4db5e456744201f7d"
283
+ "digest": "3dd132bd7d8a40f43417d2dc604af1f383a623587a288e7f104fecfde0dca3b0"
284
284
  },
285
285
  {
286
286
  "storage": "agents/skills/orkestrel-journey/SKILL.md",
@@ -352,7 +352,7 @@
352
352
  "storage": "agents/skills/orkestrel-publish/SKILL.md",
353
353
  "destination": ".agents/skills/orkestrel-publish/SKILL.md",
354
354
  "executable": false,
355
- "digest": "f4bcc24e910bbd40de268ba6a9d4ba57b3e5f9ce69075dea395479a45ec0f9d4"
355
+ "digest": "9cb5cecb878001d08fa9cd250906628a3ad0f58eabde484ca8c1dc13510503e9"
356
356
  },
357
357
  {
358
358
  "storage": "agents/skills/orkestrel-publish/agents/openai.yaml",
@@ -370,13 +370,13 @@
370
370
  "storage": "agents/skills/orkestrel-publish/references/wave.md",
371
371
  "destination": ".agents/skills/orkestrel-publish/references/wave.md",
372
372
  "executable": false,
373
- "digest": "2d5479e0d3ed0529015f5f1f21345fb389f8119333b6c5530d2320be432fe1e1"
373
+ "digest": "68f5df5747f2b5cbd950413ce3e4a070ef5be229a79811711192763cd70d73fa"
374
374
  },
375
375
  {
376
376
  "storage": "agents/skills/orkestrel-publish/references/window.md",
377
377
  "destination": ".agents/skills/orkestrel-publish/references/window.md",
378
378
  "executable": false,
379
- "digest": "734840b4803d8914bb88634f76f8c03b37da13d411a640614e446fec3c0c6ac5"
379
+ "digest": "1debb388eba46e221f99a2518de5aa94ba04ffced781a855b4a305cafc7176c3"
380
380
  },
381
381
  {
382
382
  "storage": "agents/skills/orkestrel-publish/scripts/compare.ts",
@@ -400,7 +400,7 @@
400
400
  "storage": "agents/skills/orkestrel-publish/scripts/window.ts",
401
401
  "destination": ".agents/skills/orkestrel-publish/scripts/window.ts",
402
402
  "executable": false,
403
- "digest": "143e29576afbf97e29141e4c9f38faef306929fbdfe41442c490f0d3064d119e"
403
+ "digest": "46594a58df8c07926b737373ece0fcb65e3eadfc0786bf3891542ca98e291fcc"
404
404
  },
405
405
  {
406
406
  "storage": "agents/skills/orkestrel-scout/SKILL.md",
@@ -586,7 +586,7 @@
586
586
  "storage": "claude/rules/tests.md",
587
587
  "destination": ".claude/rules/tests.md",
588
588
  "executable": false,
589
- "digest": "6913f316d2bae3bb19e579bf419c820cdba97ed70422ea5d11deb800bd07973e"
589
+ "digest": "5808e375cf559c6d51aa3a744bcea574613945629e66e304c3d2f338b2f7abc0"
590
590
  },
591
591
  {
592
592
  "storage": "claude/rules/typescript.md",
@@ -598,7 +598,7 @@
598
598
  "storage": "claude/rules/workspace.md",
599
599
  "destination": ".claude/rules/workspace.md",
600
600
  "executable": false,
601
- "digest": "3c9e269acd88935ee6c4f34dd07784f09f45bb6bfd1e27c6a183da3420da4fcc"
601
+ "digest": "f62b3c7321444d6fc30b587d9221915d0835ea3bb78204868e27beeb3ea5450b"
602
602
  },
603
603
  {
604
604
  "storage": "claude/rules/writing.md",
@@ -1042,7 +1042,7 @@
1042
1042
  "storage": "guides/scaffold.md",
1043
1043
  "destination": "guides/scaffold.md",
1044
1044
  "executable": false,
1045
- "digest": "9d6ea9f86610e3237d6ddbe120aabdec137f022092fc5311ca18e87d4b4e6212"
1045
+ "digest": "02922fe230a3d85e58ad16aa9e3b2e93afd42e9d3d66b88f60bbd68b1b90e19e"
1046
1046
  },
1047
1047
  {
1048
1048
  "storage": "guides/sea.md",
@@ -1168,7 +1168,7 @@
1168
1168
  "storage": "tests/config.test.ts",
1169
1169
  "destination": "tests/config.test.ts",
1170
1170
  "executable": false,
1171
- "digest": "5cb647597fb27442d2be85a75b446805dd33d05b9ca93808595497ab7587387f"
1171
+ "digest": "ac12462cfc786b77c23e8996c0a050f9719d870db08abe7f3a8994cbbf778e2d"
1172
1172
  },
1173
1173
  {
1174
1174
  "storage": "tests/policy.test.ts",
@@ -2144,5 +2144,5 @@
2144
2144
  ]
2145
2145
  }
2146
2146
  ],
2147
- "digest": "e53117186fbcf5d34fce9dcecef09fc8581335b11561f9ccd264a0d9f7fb52f3"
2147
+ "digest": "551ef9dbdc726896039afd3e7edc69321303f148e28cd711a6e4ba028dba2d46"
2148
2148
  }
File without changes
File without changes
File without changes
File without changes
@@ -183,7 +183,9 @@ describe('selected faces', () => {
183
183
  expect(project.test?.include).toContain(`tests/${axis}/${face}/**/*.test.ts`)
184
184
  expect(project.test?.browser).toMatchObject({
185
185
  enabled: true,
186
- instances: expect.arrayContaining([expect.objectContaining({ browser: 'chromium' })]),
186
+ instances: expect.arrayContaining([
187
+ expect.objectContaining({ browser: 'chromium', name: `${axis}:${face} (chromium)` }),
188
+ ]),
187
189
  })
188
190
  expect(project.optimizeDeps?.include).toEqual(
189
191
  expect.arrayContaining(['@orkestrel/test', '@orkestrel/test/browser']),
@@ -355,6 +357,14 @@ describe('selected faces', () => {
355
357
  width: selected.width,
356
358
  height: selected.height,
357
359
  })
360
+ expect(readConfigRecord(test.browser).instances).toEqual(
361
+ expect.arrayContaining([
362
+ expect.objectContaining({
363
+ browser: 'chromium',
364
+ name: `journey:${provide.variant} (chromium)`,
365
+ }),
366
+ ]),
367
+ )
358
368
  expect(typeof provide.capture).toBe('boolean')
359
369
  expect(readConfigRecord(project.optimizeDeps).include).toEqual(
360
370
  expect.arrayContaining(['@orkestrel/test', '@orkestrel/test/browser']),
@@ -398,6 +408,12 @@ describe('selected faces', () => {
398
408
  )
399
409
  const test = readConfigRecord(project.test)
400
410
  if (readConfigRecord(test.browser ?? {}).enabled !== true) continue
411
+ const label = typeof test.name === 'string' ? test.name : readConfigRecord(test.name).label
412
+ expect(readConfigRecord(test.browser).instances).toEqual(
413
+ expect.arrayContaining([
414
+ expect.objectContaining({ browser: 'chromium', name: `${label} (chromium)` }),
415
+ ]),
416
+ )
401
417
  const include = readConfigRecord(project.optimizeDeps).include
402
418
  expect(include).toEqual(
403
419
  expect.arrayContaining([