@orkestrel/scaffold 0.0.26 → 0.0.28
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/dist/bin/main.js +5 -1
- package/dist/bin/main.js.map +1 -1
- package/dist/host/agents/orchestration.md +183 -54
- package/dist/host/claude/agents/orkestrel.md +65 -48
- package/dist/host/claude/agents/researcher.md +1 -0
- package/dist/host/claude/agents/scout.md +1 -0
- package/dist/host/claude/rules/architecture.md +19 -0
- package/dist/host/claude/rules/documentation.md +2 -0
- package/dist/host/claude/rules/quality.md +3 -0
- package/dist/host/claude/rules/tests.md +25 -8
- package/dist/host/claude/rules/workspace.md +20 -9
- package/dist/host/guides/scaffold.md +98 -14
- package/dist/host/scripts/codex.sh +0 -0
- package/dist/host/scripts/cursor.sh +0 -0
- package/dist/host/scripts/deps.sh +0 -0
- package/dist/host/scripts/ollama.sh +0 -0
- package/dist/host/tests/config.test.ts +56 -1
- package/dist/src/core/index.cjs +167 -32
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +91 -16
- package/dist/src/core/index.d.ts +91 -16
- package/dist/src/core/index.js +164 -33
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +35 -4
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.js +36 -5
- package/dist/src/server/index.js.map +1 -1
- package/package.json +8 -8
|
@@ -110,12 +110,21 @@ environment:
|
|
|
110
110
|
The second axis is cross-cutting workspace proofs. Each one covers the whole workspace rather than
|
|
111
111
|
one environment, so each is its own project:
|
|
112
112
|
|
|
113
|
-
| Project | Files
|
|
114
|
-
| ------------- |
|
|
115
|
-
| `policy` | `tests/policy.test.ts`
|
|
116
|
-
| `config` | `tests/config.test.ts`
|
|
117
|
-
| `guides` | `tests/guides.test.ts`
|
|
118
|
-
| `
|
|
113
|
+
| Project | Files | Proves | In `test` |
|
|
114
|
+
| ------------- | ---------------------------- | --------------------------------------------------------------- | --------- |
|
|
115
|
+
| `policy` | `tests/policy.test.ts` | Every source file obeys the syntactic coding and placement law | Yes |
|
|
116
|
+
| `config` | `tests/config.test.ts` | Root configuration resolves its aliases, projects, and outputs | Yes |
|
|
117
|
+
| `guides` | `tests/guides.test.ts` | Every documented API exists and every public API is documented | Yes |
|
|
118
|
+
| `conformance` | `tests/conformance.test.ts` | Where this package drifts from the official tooling it tracks | Yes |
|
|
119
|
+
| `integration` | `tests/integration.test.ts` | The built package works when installed and driven from outside | No |
|
|
120
|
+
| `service` | `tests/service/**/*.test.ts` | The live external services this package drives, driven for real | No |
|
|
121
|
+
|
|
122
|
+
`conformance` and `service` are two subjects, not two names for one. `conformance` measures this
|
|
123
|
+
package against an official artifact it stays compatible with and drives nothing external: the
|
|
124
|
+
tooling it measures against is installed, and any server it drives is one the proof starts itself.
|
|
125
|
+
That is what makes it hermetic and keeps it in `test`. `service` drives the real thing, so it takes
|
|
126
|
+
`tests/setupService.ts` for readiness, longer timeouts, and no file parallelism, and it leaves
|
|
127
|
+
`test` for `prepublishOnly`.
|
|
119
128
|
|
|
120
129
|
One project sits on neither axis. `probe` includes `tmp/probe/**/*.test.ts` so an agent can run a
|
|
121
130
|
throwaway instrument against real sources, aliases and setup. Declare no proof there. Every test
|
|
@@ -123,8 +132,9 @@ script names its project, so no gate runs it; its directory is ignored by git; a
|
|
|
123
132
|
`.claude/rules/tests.md` governs what may live there.
|
|
124
133
|
|
|
125
134
|
- Define a cross-cutting project only for a proof the package actually has.
|
|
126
|
-
- A live-service project is the fifth kind. It is
|
|
127
|
-
`.claude/rules/tests.md` governs it.
|
|
135
|
+
- A live-service project is the fifth kind. It is the `service` project above, `scripts/service.sh`
|
|
136
|
+
provisions what it drives, and `.claude/rules/tests.md` governs it. Name it `service` whatever it
|
|
137
|
+
drives.
|
|
128
138
|
- A project leaves the default run for one of two reasons: it drives a live external service, or it
|
|
129
139
|
is hermetic but slow — it spawns processes, packs, installs, or drives a real build.
|
|
130
140
|
- Every isolated project has its own script, is excluded from `test`, and runs in `prepublishOnly`.
|
|
@@ -137,7 +147,7 @@ Setup assets:
|
|
|
137
147
|
|
|
138
148
|
Scope with `test:src`, `test:src:core`, `test:app`, `test:app:server`, and equivalent scripts. Each
|
|
139
149
|
cross-cutting project has its own script too: `test:policy`, `test:config`, `test:guides`,
|
|
140
|
-
`test:integration`.
|
|
150
|
+
`test:conformance`, `test:integration`, `test:service`.
|
|
141
151
|
|
|
142
152
|
## Typechecking and environment isolation
|
|
143
153
|
|
|
@@ -211,3 +221,4 @@ Run `show` only **after** formatting. The committed `demo/showcase.html` is gene
|
|
|
211
221
|
- Store text as UTF-8.
|
|
212
222
|
- Before accepting broad generated or migrated edits, scan changed text for replacement characters, mojibake, unintended control characters, and accidental trailing debris.
|
|
213
223
|
- Preserve intentional Unicode punctuation and symbols; do not “clean” valid text merely because it is non-ASCII.
|
|
224
|
+
- Never renormalize Unicode while rewriting a file. Retyping a line can silently fold a decomposed sequence into its precomposed form — `e` + U+0301 becoming U+00E9 — and the two render identically, so the diff reads as a no-op and review sees nothing. Where the exact code points are the subject, as in an encoding or transport proof, that fold deletes the case the test exists for while leaving it green and named. Move such a line rather than retyping it, and compare bytes with `od -c` or a code-point dump rather than by eye.
|
|
@@ -92,6 +92,7 @@ Exported from `@orkestrel/scaffold`, and reachable from
|
|
|
92
92
|
| `BIN_ENTRY_PATH` | const | The executable entry whose presence makes a workspace `bin`. |
|
|
93
93
|
| `CATALOG_AGENT_PATH` | const | The agent file whose marker-bounded package table the catalog verb alone owns. |
|
|
94
94
|
| `CONFIG_TEMPLATES` | const | Formatter-stable template text for every configuration artifact. |
|
|
95
|
+
| `CONFORMANCE_TEST_PATH` | const | The official-tooling drift proof whose presence makes a workspace `conformance`. |
|
|
95
96
|
| `CONTROL_CHARACTER_PATTERN` | const | Unicode controls, formatting controls, and line and paragraph separators rejected in text. |
|
|
96
97
|
| `DEFAULT_ENGINES` | const | The `engines.node` range a workspace starts with. |
|
|
97
98
|
| `DEFAULT_VERSION` | const | The version a workspace starts at. |
|
|
@@ -124,6 +125,8 @@ Exported from `@orkestrel/scaffold`, and reachable from
|
|
|
124
125
|
| `ORCHESTRATION_PATH_PREFIXES` | const | The path prefixes whose contents instruct or wire an agent, frozen. |
|
|
125
126
|
| `ORKESTREL_RANGE_PATTERN` | const | The exact caret-pinned pre-1.0 range accepted for an `@orkestrel/*` runtime dependency. |
|
|
126
127
|
| `SERVICE_SCRIPT_PATH` | const | The provisioner skeleton a workspace with declared service vendors is given once. |
|
|
128
|
+
| `SERVICE_SETUP_PATH` | const | The live-service readiness module whose presence makes a workspace `service`. |
|
|
129
|
+
| `SERVICE_TEST_INCLUDE` | const | The include the live-service project covers, which is a directory rather than one proof. |
|
|
127
130
|
| `SHOWCASE_CONFIG_PATH` | const | The Vite wrapper whose presence makes a workspace `showcase`. |
|
|
128
131
|
| `SHOWCASE_DEV_DEPENDENCIES` | const | The development dependency used only by the optional single-file showcase build. |
|
|
129
132
|
| `SOURCE_BROWSER_DEV_DEPENDENCIES` | const | The development dependencies a published browser `src` environment adds. |
|
|
@@ -172,6 +175,7 @@ Exported from `@orkestrel/scaffold`, and reachable from
|
|
|
172
175
|
| --------------------------- | -------- | ----------------------------------------------------------------------------- |
|
|
173
176
|
| `artifactToHex` | function | Project an artifact to the exact bytes it claims, as hexadecimal. |
|
|
174
177
|
| `bytesToHex` | function | Encode bytes as exact lowercase hexadecimal text. |
|
|
178
|
+
| `catalogToLayers` | function | Project a catalog into the layers it publishes in. |
|
|
175
179
|
| `cloneValue` | function | Snapshot an untrusted value into exact JSON data the caller owns. |
|
|
176
180
|
| `compareVersions` | function | Compare two versions by their numeric components. |
|
|
177
181
|
| `computeBytes` | function | Count the UTF-8 bytes text encodes to. |
|
|
@@ -455,25 +459,33 @@ workspace would otherwise declare a dependency that does not resolve.
|
|
|
455
459
|
|
|
456
460
|
`new --bin` creates the executable entry, its test, and its scoped Vite and TypeScript wrappers.
|
|
457
461
|
The other structural facts do not need creation flags. Add `tests/integration.test.ts` for
|
|
458
|
-
`integration`, `tests/
|
|
459
|
-
`configs/app/vite.showcase.config.ts` for `showcase`;
|
|
460
|
-
register its fixed machinery. Add `scripts/service.sh`
|
|
461
|
-
protect that birth-owned script, but do not infer its
|
|
462
|
+
`integration`, `tests/conformance.test.ts` for `conformance`, `tests/setupService.ts` for `service`,
|
|
463
|
+
`tests/setupGlobal.ts` for `global`, and `configs/app/vite.showcase.config.ts` for `showcase`;
|
|
464
|
+
reading verbs detect each exact-case file and register its fixed machinery. Add `scripts/service.sh`
|
|
465
|
+
for `vendors`. Reading verbs preserve and protect that birth-owned script, but do not infer its
|
|
466
|
+
vendor list from edited text.
|
|
462
467
|
|
|
463
468
|
### Reading a target
|
|
464
469
|
|
|
465
470
|
`audit`, `repair`, `catalog`, and `overwrite` derive the blueprint from the target itself. The name
|
|
466
471
|
and the declared `@orkestrel/*` packages come from `package.json`. The two environment axes come
|
|
467
472
|
from the directories the target actually ships, because a directory is the fact and a declaration
|
|
468
|
-
beside it could disagree.
|
|
469
|
-
`bin`, `tests/integration.test.ts` selects `integration`, `tests/
|
|
473
|
+
beside it could disagree. Six more facts come from exact-case files: `src/bin/main.ts` selects
|
|
474
|
+
`bin`, `tests/integration.test.ts` selects `integration`, `tests/conformance.test.ts` selects
|
|
475
|
+
`conformance`, `tests/setupService.ts` selects `service`, `tests/setupGlobal.ts` selects `global`,
|
|
470
476
|
and `configs/app/vite.showcase.config.ts` selects `showcase`. A containing directory does not select
|
|
471
477
|
the fact by itself.
|
|
472
478
|
|
|
473
|
-
`
|
|
474
|
-
script text is not a trustworthy declaration of a
|
|
479
|
+
`vendors` is not reconstructed. Its only artifact, `scripts/service.sh`, is birth-owned, so edited
|
|
480
|
+
script text is not a trustworthy declaration of a vendor list. A present script remains in the
|
|
475
481
|
target and remains protected from deletion through the owned scripts inventory, but a reading verb
|
|
476
|
-
does not infer
|
|
482
|
+
does not infer vendors from it.
|
|
483
|
+
|
|
484
|
+
That is why the live-service project follows `service` rather than `vendors`. A reading verb has to
|
|
485
|
+
plan the project before it can say anything about a target that runs one, and a vendor list it
|
|
486
|
+
cannot recover would leave every such workspace unplannable. `tests/setupService.ts` is recoverable,
|
|
487
|
+
is the module the root configuration names by path, and is what a live proof needs anyway, so it
|
|
488
|
+
carries the fact and the vendor list keeps its own separate job.
|
|
477
489
|
|
|
478
490
|
The root Vite configuration always defines the fixed `guides` project and selects it at
|
|
479
491
|
configuration load only when `tests/guides.test.ts` is a physical file with that exact path case. A
|
|
@@ -559,9 +571,16 @@ because the shape is chosen once and read afterwards: `new` refuses the advisory
|
|
|
559
571
|
`repair` need the plan to describe and restore a target that already has that shape. A library
|
|
560
572
|
caller creating a workspace holds the same refusal, and the Compile section below states it.
|
|
561
573
|
|
|
562
|
-
`bin`, `integration`, `
|
|
563
|
-
when the workspace physically ships the directory or exact-case file that
|
|
564
|
-
of the workspace's name and never because a sibling fact is set.
|
|
574
|
+
`bin`, `integration`, `conformance`, `service`, `vendors`, `global`, and `showcase` are structural
|
|
575
|
+
facts. Each is set only when the workspace physically ships the directory or exact-case file that
|
|
576
|
+
defines it, never because of the workspace's name and never because a sibling fact is set.
|
|
577
|
+
|
|
578
|
+
`service` says the workspace runs a live-service Vitest project over `tests/service`, and it alone
|
|
579
|
+
registers that project, its `test:service` script, and the `tests/setupService.ts` readiness module
|
|
580
|
+
the project names. `vendors` names each external service the workspace drives and emits
|
|
581
|
+
`scripts/service.sh`, the provisioner that starts them. Neither is derivable from the other: a
|
|
582
|
+
workspace may declare vendors before it writes a suite, and a suite may drive a service the skeleton
|
|
583
|
+
does not start.
|
|
565
584
|
|
|
566
585
|
An axis-dependent structural fact projects only when its required axis exists. `integration`
|
|
567
586
|
projects a published `src`, and `showcase` projects the browser `app` environment. When that axis is
|
|
@@ -673,7 +692,8 @@ barrels, the tests, `README.md`, and `guides/README.md` are written once and are
|
|
|
673
692
|
a later verb.
|
|
674
693
|
|
|
675
694
|
Content ownership does not preserve an arbitrary custom Vitest project. Fixed optional proofs are
|
|
676
|
-
selected by their defining paths, as `guides`
|
|
695
|
+
selected by their defining paths, as `guides`, `conformance`, and `service` are. A workspace that
|
|
696
|
+
needs other local configuration
|
|
677
697
|
must keep those edits outside a content-owned file; `repair` restores that file to the canonical
|
|
678
698
|
project set.
|
|
679
699
|
|
|
@@ -723,6 +743,46 @@ older planned finding refuses that call too. Take a fresh audit rather than repl
|
|
|
723
743
|
a stored audit records what a target looked like then, and both verbs bind their writes to what a
|
|
724
744
|
target holds now. The refusal is deliberate at `0.0.x` and there is no migration path.
|
|
725
745
|
|
|
746
|
+
## Fleet catalog
|
|
747
|
+
|
|
748
|
+
`catalog` rewrites one marker-bounded region in `CATALOG_AGENT_PATH` and nothing else in that file.
|
|
749
|
+
The region holds a table with four columns:
|
|
750
|
+
|
|
751
|
+
| Column | Content |
|
|
752
|
+
| ---------------------- | -------------------------------------------------------------------------- |
|
|
753
|
+
| `Package` | The published package name |
|
|
754
|
+
| `Version` | The registry's `dist-tags.latest`, or the cause when the lookup found none |
|
|
755
|
+
| `Layer` | The publish round the edges place the package in, as `L0`, `L1`, … |
|
|
756
|
+
| `Runtime dependencies` | Each declared runtime edge, as name and range |
|
|
757
|
+
|
|
758
|
+
Both edge-bearing columns come from the same abbreviated packument the version came from, so a
|
|
759
|
+
catalog costs one request per package and no more. Only `dependencies` is read. `devDependencies`
|
|
760
|
+
reaches no consumer of the published package, so it constrains nothing about publish order, and
|
|
761
|
+
reading it would place packages in rounds that do not exist.
|
|
762
|
+
|
|
763
|
+
The layer is not stored on a row. `catalogToLayers` derives it from the rows' own edges, in the same
|
|
764
|
+
call that writes them, so the two cannot disagree:
|
|
765
|
+
|
|
766
|
+
```ts
|
|
767
|
+
import { catalogToLayers } from '@orkestrel/scaffold'
|
|
768
|
+
|
|
769
|
+
const layers = catalogToLayers(entries)
|
|
770
|
+
layers[0] // the names that depend on nothing else in the fleet
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
An edge counts only when it names a package this catalog publishes. An edge leaving the fleet and an
|
|
774
|
+
edge to a row that found no version each constrain nothing, so neither holds its dependent back.
|
|
775
|
+
|
|
776
|
+
The order is load-bearing because these packages are `0.0.x`, where a caret pins one exact release.
|
|
777
|
+
A dependent sees a new dependency version only after the dependent re-pins and republishes, so
|
|
778
|
+
publishing a dependent before its dependency leaves the dependent pinned to the older release. Two
|
|
779
|
+
ranges that disagree install two copies of one package, and the compiler reads those copies as two
|
|
780
|
+
distinct types.
|
|
781
|
+
|
|
782
|
+
A cycle cannot be published in rounds. `catalogToLayers` omits its members rather than placing them
|
|
783
|
+
in an order that would be wrong, and their rows carry no layer cell. An absent name is the report:
|
|
784
|
+
compare the returned names against the catalog to find one.
|
|
785
|
+
|
|
726
786
|
## Vendored data root
|
|
727
787
|
|
|
728
788
|
The vendored data root is the shared file set, staged into the published package as plain data. It
|
|
@@ -919,7 +979,7 @@ are thrown, so an observer sees a refusal even where the caller catches it.
|
|
|
919
979
|
|
|
920
980
|
## Limits
|
|
921
981
|
|
|
922
|
-
|
|
982
|
+
Seven things a reader will look for and not find.
|
|
923
983
|
|
|
924
984
|
**A code fence in this guide is unverified.** [`tests/guides.test.ts`](../tests/guides.test.ts)
|
|
925
985
|
proves that every fence imports only real exports of the two barrels, and that every backticked name
|
|
@@ -971,6 +1031,30 @@ nothing. This is deliberate: a generated sample entity is repeatedly mistaken fo
|
|
|
971
1031
|
implementation. What a consumer does first is write the module's `types.ts`, then the
|
|
972
1032
|
implementation that conforms to it, then export both from the barrel — the order `AGENTS.md` fixes.
|
|
973
1033
|
|
|
1034
|
+
**A conformance or live-service proof is registered, but neither is written for you.** Scaffold
|
|
1035
|
+
registers the `conformance` and `service` projects, their scripts, and the gate each belongs to. It
|
|
1036
|
+
emits no proof into either, because both name something only the package knows: the official
|
|
1037
|
+
artifact a conformance check measures against, and the service a live proof drives. A generated
|
|
1038
|
+
placeholder would read as a proof while measuring nothing, so the file a consumer writes is the file
|
|
1039
|
+
that selects the project.
|
|
1040
|
+
|
|
1041
|
+
The consequence is one empty-project case in each direction. A blueprint carrying `conformance` with
|
|
1042
|
+
no `tests/conformance.test.ts` registers a project whose include resolves to nothing, and Vitest
|
|
1043
|
+
exits non-zero on it. A blueprint carrying `service` gets `tests/setupService.ts` — the root
|
|
1044
|
+
configuration names that module by path, so an absent one fails the project's load rather than its
|
|
1045
|
+
run — and still no suite beneath `tests/service`, so `test:service` reports no test files until the
|
|
1046
|
+
consumer writes the first one. Both cases are visible the first time the script runs, which is why
|
|
1047
|
+
neither is silent.
|
|
1048
|
+
|
|
1049
|
+
Neither project folds into `integration`, which means something narrower: the built package works
|
|
1050
|
+
when installed and driven from outside. Two fleet packages hold the distinction. `@orkestrel/ollama`
|
|
1051
|
+
drives a real Ollama daemon through a `service` project, so a real service answers it and it runs
|
|
1052
|
+
from `prepublishOnly`. `@orkestrel/mcp` measures its server against the specification's own runner,
|
|
1053
|
+
`@modelcontextprotocol/conformance`, through a `conformance` project. It pins that runner as a
|
|
1054
|
+
development dependency and resolves it out of `node_modules`, and the server the runner drives is
|
|
1055
|
+
one the fixture starts itself on a loopback port, so the run drives nothing external and stays in
|
|
1056
|
+
`test`.
|
|
1057
|
+
|
|
974
1058
|
## Tests
|
|
975
1059
|
|
|
976
1060
|
- [`tests/src/core/Compiler.test.ts`](../tests/src/core/Compiler.test.ts) — the three stages, the
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -102,13 +102,22 @@ describe('root configuration', () => {
|
|
|
102
102
|
setup: ['./tests/setup.ts', './tests/setupServer.ts'],
|
|
103
103
|
})
|
|
104
104
|
}
|
|
105
|
-
for (const label of ['policy', 'config', 'guides', 'integration']) {
|
|
105
|
+
for (const label of ['policy', 'config', 'guides', 'conformance', 'integration']) {
|
|
106
106
|
if (!existsSync(resolve(root, `tests/${label}.test.ts`))) continue
|
|
107
107
|
expected.set(label, {
|
|
108
108
|
include: `tests/${label}.test.ts`,
|
|
109
109
|
setup: ['./tests/setup.ts'],
|
|
110
110
|
})
|
|
111
111
|
}
|
|
112
|
+
// The live-service project covers a directory rather than one proof, so its
|
|
113
|
+
// readiness module is the fact that selects it. A suite beneath
|
|
114
|
+
// `tests/service` with no setup module is a project nothing configures.
|
|
115
|
+
if (existsSync(resolve(root, 'tests/setupService.ts'))) {
|
|
116
|
+
expected.set('service', {
|
|
117
|
+
include: 'tests/service/**/*.test.ts',
|
|
118
|
+
setup: ['./tests/setup.ts', './tests/setupService.ts'],
|
|
119
|
+
})
|
|
120
|
+
}
|
|
112
121
|
expected.set('probe', { include: 'tmp/probe/**/*.test.ts', setup: ['./tests/setup.ts'] })
|
|
113
122
|
// A row that is a configuration rather than a factory. A workspace with a
|
|
114
123
|
// browser application emits one, because that factory refuses overrides and
|
|
@@ -353,8 +362,35 @@ describe('root configuration', () => {
|
|
|
353
362
|
const test = Object.getOwnPropertyDescriptor(scripts, 'test')?.value
|
|
354
363
|
const config = Object.getOwnPropertyDescriptor(scripts, 'test:config')?.value
|
|
355
364
|
const integration = Object.getOwnPropertyDescriptor(scripts, 'test:integration')?.value
|
|
365
|
+
const conformance = Object.getOwnPropertyDescriptor(scripts, 'test:conformance')?.value
|
|
366
|
+
const service = Object.getOwnPropertyDescriptor(scripts, 'test:service')?.value
|
|
356
367
|
const publish = Object.getOwnPropertyDescriptor(scripts, 'prepublishOnly')?.value
|
|
357
368
|
const hasIntegration = existsSync(resolve(root, 'tests/integration.test.ts'))
|
|
369
|
+
// The optional proofs are read off the registered project set rather than off
|
|
370
|
+
// their files, because the defect this measures is a registered project no
|
|
371
|
+
// gate runs. A project selected by a path that is not yet there is still
|
|
372
|
+
// registered, and it is exactly the one whose script goes missing.
|
|
373
|
+
const rows = configuration.test?.projects
|
|
374
|
+
if (!Array.isArray(rows)) throw new Error('The root configuration carries no projects')
|
|
375
|
+
const registered = new Set<string>()
|
|
376
|
+
for (const row of rows) {
|
|
377
|
+
if (typeof row === 'function') {
|
|
378
|
+
registered.add(row.name)
|
|
379
|
+
continue
|
|
380
|
+
}
|
|
381
|
+
if (typeof row !== 'object' || row === null) continue
|
|
382
|
+
const block: unknown = Object.getOwnPropertyDescriptor(row, 'test')?.value
|
|
383
|
+
if (typeof block !== 'object' || block === null) continue
|
|
384
|
+
const named: unknown = Object.getOwnPropertyDescriptor(block, 'name')?.value
|
|
385
|
+
if (typeof named !== 'object' || named === null) continue
|
|
386
|
+
const label: unknown = Object.getOwnPropertyDescriptor(named, 'label')?.value
|
|
387
|
+
if (typeof label === 'string') registered.add(label)
|
|
388
|
+
}
|
|
389
|
+
// The population must be able to answer both ways before either answer counts.
|
|
390
|
+
expect(registered.has('config')).toBe(true)
|
|
391
|
+
expect(registered.has('control')).toBe(false)
|
|
392
|
+
const hasConformance = registered.has('conformance')
|
|
393
|
+
const hasService = registered.has('service')
|
|
358
394
|
expect(config).toBe(
|
|
359
395
|
'vitest run --config vite.config.ts --no-cache --reporter=dot --project config',
|
|
360
396
|
)
|
|
@@ -368,6 +404,25 @@ describe('root configuration', () => {
|
|
|
368
404
|
expect(typeof publish === 'string' && publish.includes('npm run test:integration')).toBe(
|
|
369
405
|
hasIntegration,
|
|
370
406
|
)
|
|
407
|
+
// A registered project no gate runs is a proof that never executes, and it
|
|
408
|
+
// never fails, so the suite reports green while carrying it. Conformance is
|
|
409
|
+
// hermetic and belongs to `test`; the live-service project drives a real
|
|
410
|
+
// service and belongs to `prepublishOnly` alone.
|
|
411
|
+
expect(conformance).toBe(
|
|
412
|
+
hasConformance
|
|
413
|
+
? 'vitest run --config vite.config.ts --no-cache --reporter=dot --project conformance'
|
|
414
|
+
: undefined,
|
|
415
|
+
)
|
|
416
|
+
expect(typeof test === 'string' && test.includes('npm run test:conformance')).toBe(
|
|
417
|
+
hasConformance,
|
|
418
|
+
)
|
|
419
|
+
expect(service).toBe(
|
|
420
|
+
hasService
|
|
421
|
+
? 'vitest run --config vite.config.ts --no-cache --reporter=dot --project service'
|
|
422
|
+
: undefined,
|
|
423
|
+
)
|
|
424
|
+
expect(typeof test === 'string' && test.includes('test:service')).toBe(false)
|
|
425
|
+
expect(typeof publish === 'string' && publish.includes('npm run test:service')).toBe(hasService)
|
|
371
426
|
})
|
|
372
427
|
})
|
|
373
428
|
|