@aura3d/cli 1.4.5 → 1.5.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/dist/asset-intent.d.ts +148 -0
- package/dist/asset-intent.d.ts.map +1 -0
- package/dist/asset-intent.js +145 -0
- package/dist/asset-intent.js.map +1 -0
- package/dist/asset-role-admission.d.ts +174 -0
- package/dist/asset-role-admission.d.ts.map +1 -0
- package/dist/asset-role-admission.js +381 -0
- package/dist/asset-role-admission.js.map +1 -0
- package/dist/asset-screening-effects.d.ts +95 -0
- package/dist/asset-screening-effects.d.ts.map +1 -0
- package/dist/asset-screening-effects.js +321 -0
- package/dist/asset-screening-effects.js.map +1 -0
- package/dist/asset-screening-pipeline.d.ts +132 -0
- package/dist/asset-screening-pipeline.d.ts.map +1 -0
- package/dist/asset-screening-pipeline.js +277 -0
- package/dist/asset-screening-pipeline.js.map +1 -0
- package/dist/cartoon-asset-profiles.d.ts +15 -0
- package/dist/cartoon-asset-profiles.d.ts.map +1 -0
- package/dist/cartoon-asset-profiles.js +46 -0
- package/dist/cartoon-asset-profiles.js.map +1 -0
- package/dist/cli-help.js +1 -1
- package/dist/cli.js +16 -2
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +390 -17
- package/dist/index.js.map +1 -1
- package/dist/pull-bridge/search.d.ts +4 -0
- package/dist/pull-bridge/search.d.ts.map +1 -1
- package/dist/pull-bridge/search.js +22 -0
- package/dist/pull-bridge/search.js.map +1 -1
- package/dist/pull-bridge.d.ts +20 -5
- package/dist/pull-bridge.d.ts.map +1 -1
- package/dist/pull-bridge.js +38 -1
- package/dist/pull-bridge.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Role-aware asset fitness admission.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this exists, and why one boolean is not enough
|
|
5
|
+
*
|
|
6
|
+
* Three hero-vehicle assets shipped into Turbo Drift Circuit and every one of them was visually wrong
|
|
7
|
+
* in a *different* way that every existing gate accepted:
|
|
8
|
+
*
|
|
9
|
+
* | Asset | Parts | Triangles | Actual failure |
|
|
10
|
+
* | --- | --- | --- | --- |
|
|
11
|
+
* | `showcaseTexturedSportsCar` | 7 | 33,700 | tyres attached through detached stalk-like geometry |
|
|
12
|
+
* | `showcaseCityVehicle` | 1 | 792 | a distant low-poly body shell with no wheels modelled at all |
|
|
13
|
+
* | `turboHeroCar` | 483 | 71,426 | closed-wheel Le Mans body encloses the wheels; not readable |
|
|
14
|
+
*
|
|
15
|
+
* Nothing caught them because every upstream check measured the *frame*, not the *model*: probe
|
|
16
|
+
* thresholds measure subject pixel size, readability rules measure foreground bounds, composition
|
|
17
|
+
* checks measure coverage ratios. A wheelless car is a large, well-lit, correctly-framed subject and
|
|
18
|
+
* passes all of them.
|
|
19
|
+
*
|
|
20
|
+
* The second lesson is that fitness is **not global**. `showcaseCityVehicle` is a perfectly good
|
|
21
|
+
* background traffic prop and a bad hero. `turboHeroCar` is a structurally valid vehicle and a bad
|
|
22
|
+
* choice when the requested style needs exposed tyres. Collapsing that into one pass/fail either
|
|
23
|
+
* rejects usable assets or admits unusable ones. So admission is evaluated **against a requested role
|
|
24
|
+
* and its visual requirements**, and every check is reported individually with its own verdict so a
|
|
25
|
+
* rejection is machine-readable and explains itself.
|
|
26
|
+
*
|
|
27
|
+
* ## Deliberate boundary
|
|
28
|
+
*
|
|
29
|
+
* This module is structural only. It consumes geometry and provenance facts and never claims rendered
|
|
30
|
+
* visibility: `wheelsVisibleInSilhouette` is a *geometric prediction* that wheels reach the body
|
|
31
|
+
* silhouette, not proof a viewer saw them. Rendered proof is a separate input
|
|
32
|
+
* (`renderedWheelVisibility`) supplied by a browser probe, because the repository rule is that no
|
|
33
|
+
* visual claim may rest on mesh statistics. When rendered evidence is absent for a role that requires
|
|
34
|
+
* it, admission reports `unproven` rather than `pass`.
|
|
35
|
+
*/
|
|
36
|
+
/** Minimum distinct outboard corners for a vehicle to count as wheeled. */
|
|
37
|
+
export const MIN_VEHICLE_WHEEL_CORNERS = 3;
|
|
38
|
+
/** Below this triangle count an asset is a distant prop, not a hero subject. */
|
|
39
|
+
export const HERO_MIN_TRIANGLES = 3_000;
|
|
40
|
+
/**
|
|
41
|
+
* A hero render must cover at least this many distinct azimuths.
|
|
42
|
+
*
|
|
43
|
+
* One angle is not evidence: the `turboRaceCar` release probe rendered a dead-on front view (camera
|
|
44
|
+
* azimuth and node rotation cancelled exactly), which is the single angle where a car's own bodywork
|
|
45
|
+
* hides its wheels. That frame was read as "the renderer is dropping the wheel primitives" when in
|
|
46
|
+
* fact all five primitives were drawing. Requiring multiple angles makes that mistake impossible.
|
|
47
|
+
*/
|
|
48
|
+
export const HERO_MIN_RENDERED_AZIMUTHS = 2;
|
|
49
|
+
/**
|
|
50
|
+
* Evaluate an asset against a requested role.
|
|
51
|
+
*
|
|
52
|
+
* Returns every check individually. Callers must not reduce this to a boolean without also surfacing
|
|
53
|
+
* `blockers` and `unproven`, or the "asset loads, therefore asset is fine" failure returns.
|
|
54
|
+
*/
|
|
55
|
+
export function admitAssetForRole(input) {
|
|
56
|
+
const { requirement: requirement, geometry, rendered, provenance } = input;
|
|
57
|
+
const checks = [];
|
|
58
|
+
const isVehicle = requirement.role === "hero-vehicle" || requirement.role === "background-vehicle";
|
|
59
|
+
const isHero = requirement.role === "hero-vehicle" || requirement.role === "playable-character" || requirement.role === "ui-hero-object";
|
|
60
|
+
checks.push({
|
|
61
|
+
id: "part-count",
|
|
62
|
+
verdict: geometry.partCount > 0 ? "pass" : "fail",
|
|
63
|
+
detail: geometry.partCount > 0 ? `${geometry.partCount} mesh part(s)` : "no mesh parts with readable bounds",
|
|
64
|
+
measured: geometry.partCount
|
|
65
|
+
});
|
|
66
|
+
/*
|
|
67
|
+
* The hero triangle floor applies only when a triangle count was actually measured.
|
|
68
|
+
*
|
|
69
|
+
* Callers that read structural facts from a manifest have no triangle count available. Defaulting a
|
|
70
|
+
* missing count to `0` and then failing the floor reports "distant-prop shell" for *absent metadata*,
|
|
71
|
+
* which is a false accusation: it fired on correctly-certified release fixtures. A floor can only be
|
|
72
|
+
* enforced against a real measurement, so `triangles === 0` disables the implied hero floor while an
|
|
73
|
+
* explicitly requested `minTriangles` is still honoured.
|
|
74
|
+
*/
|
|
75
|
+
const minTriangles = requirement.minTriangles ?? (isHero && geometry.triangles > 0 ? HERO_MIN_TRIANGLES : undefined);
|
|
76
|
+
if (minTriangles !== undefined) {
|
|
77
|
+
const ok = geometry.triangles >= minTriangles;
|
|
78
|
+
checks.push({
|
|
79
|
+
id: "triangle-floor",
|
|
80
|
+
verdict: ok ? "pass" : "fail",
|
|
81
|
+
detail: ok
|
|
82
|
+
? `${geometry.triangles} triangles meets the ${minTriangles} floor for ${requirement.role}`
|
|
83
|
+
: `${geometry.triangles} triangles is below the ${minTriangles} floor for ${requirement.role}; this is a distant-prop shell, not a hero subject`,
|
|
84
|
+
measured: geometry.triangles
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
if (requirement.maxTriangles !== undefined) {
|
|
88
|
+
const ok = geometry.triangles <= requirement.maxTriangles;
|
|
89
|
+
checks.push({
|
|
90
|
+
id: "triangle-budget",
|
|
91
|
+
verdict: ok ? "pass" : "fail",
|
|
92
|
+
detail: ok
|
|
93
|
+
? `${geometry.triangles} triangles within the ${requirement.maxTriangles} budget`
|
|
94
|
+
: `${geometry.triangles} triangles exceeds the ${requirement.maxTriangles} budget`,
|
|
95
|
+
measured: geometry.triangles
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
if (requirement.requireTextured) {
|
|
99
|
+
const textures = geometry.textureCount ?? 0;
|
|
100
|
+
checks.push({
|
|
101
|
+
id: "textured",
|
|
102
|
+
verdict: textures > 0 ? "pass" : "fail",
|
|
103
|
+
detail: textures > 0 ? `${textures} texture(s)` : "no textures; the role requires textured materials rather than flat colour",
|
|
104
|
+
measured: textures
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
if (isVehicle) {
|
|
108
|
+
const corners = geometry.distinctWheelCorners ?? 0;
|
|
109
|
+
const candidates = geometry.wheelCandidates ?? 0;
|
|
110
|
+
const wheeled = corners >= MIN_VEHICLE_WHEEL_CORNERS;
|
|
111
|
+
/*
|
|
112
|
+
* Distinguish "measured and absent" from "never measured".
|
|
113
|
+
*
|
|
114
|
+
* Wheel-part detection requires reading the mesh; a caller working from manifest metadata cannot
|
|
115
|
+
* supply it. Reporting `fail` in that case accused correctly-certified assets of having no wheels.
|
|
116
|
+
* `unproven` is the honest verdict when neither field was provided, and it still blocks admission --
|
|
117
|
+
* it simply does not claim the asset is wheelless.
|
|
118
|
+
*/
|
|
119
|
+
const wheelGeometryMeasured = geometry.wheelCandidates !== undefined || geometry.distinctWheelCorners !== undefined;
|
|
120
|
+
checks.push({
|
|
121
|
+
id: "wheel-geometry-present",
|
|
122
|
+
verdict: wheeled ? "pass" : wheelGeometryMeasured ? "fail" : "unproven",
|
|
123
|
+
detail: wheeled
|
|
124
|
+
? `${candidates} wheel-like part(s) across ${corners} corner(s)`
|
|
125
|
+
: !wheelGeometryMeasured
|
|
126
|
+
? "wheel geometry was not measured; supply wheelCandidates/distinctWheelCorners from a mesh audit"
|
|
127
|
+
: candidates === 0
|
|
128
|
+
? "no roughly-circular low-mounted parts: this is a body shell with no wheels modelled"
|
|
129
|
+
: `wheel-like parts at only ${corners} corner(s); a vehicle needs ${MIN_VEHICLE_WHEEL_CORNERS}+`,
|
|
130
|
+
measured: corners
|
|
131
|
+
});
|
|
132
|
+
// Deliberately separate from the check above: existence is not readability.
|
|
133
|
+
if (requirement.requireReadableWheels) {
|
|
134
|
+
const silhouette = geometry.wheelsVisibleInSilhouette;
|
|
135
|
+
checks.push({
|
|
136
|
+
id: "wheels-outside-body-silhouette",
|
|
137
|
+
verdict: silhouette === undefined ? "unproven" : silhouette ? "pass" : "fail",
|
|
138
|
+
detail: silhouette === undefined
|
|
139
|
+
? "wheel/body silhouette relationship was not measured"
|
|
140
|
+
: silhouette
|
|
141
|
+
? `wheels reach the body silhouette (wheel half-width ${fmt(geometry.wheelHalfWidth)} vs body ${fmt(geometry.bodyHalfWidth)})`
|
|
142
|
+
: `wheels are enclosed inside the bodywork (wheel half-width ${fmt(geometry.wheelHalfWidth)} vs body ${fmt(geometry.bodyHalfWidth)}); they exist but cannot be seen, so this reads as a closed-wheel prototype`,
|
|
143
|
+
measured: silhouette
|
|
144
|
+
});
|
|
145
|
+
// Structural prediction is never accepted as rendered proof.
|
|
146
|
+
const renderedVisible = rendered?.renderedWheelVisibility;
|
|
147
|
+
const azimuths = rendered?.renderedAzimuths?.length ?? 0;
|
|
148
|
+
if (renderedVisible === undefined) {
|
|
149
|
+
checks.push({
|
|
150
|
+
id: "rendered-wheel-visibility",
|
|
151
|
+
verdict: "unproven",
|
|
152
|
+
detail: "no retained render measures wheel visibility; geometry alone cannot prove a viewer sees the tyres",
|
|
153
|
+
measured: false
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
else if (!renderedVisible) {
|
|
157
|
+
checks.push({
|
|
158
|
+
id: "rendered-wheel-visibility",
|
|
159
|
+
verdict: "fail",
|
|
160
|
+
detail: `retained render${rendered?.screenshotPath ? ` ${rendered.screenshotPath}` : ""} does not show readable wheels`,
|
|
161
|
+
measured: false
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
else if (azimuths < HERO_MIN_RENDERED_AZIMUTHS) {
|
|
165
|
+
checks.push({
|
|
166
|
+
id: "rendered-wheel-visibility",
|
|
167
|
+
verdict: "unproven",
|
|
168
|
+
detail: `wheels read at only ${azimuths} camera angle(s); ${HERO_MIN_RENDERED_AZIMUTHS}+ are required because a single head-on view is the one angle where bodywork hides its own wheels`,
|
|
169
|
+
measured: azimuths
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
checks.push({
|
|
174
|
+
id: "rendered-wheel-visibility",
|
|
175
|
+
verdict: "pass",
|
|
176
|
+
detail: `wheels read across ${azimuths} camera angles in ${rendered?.screenshotPath ?? "the retained render"}`,
|
|
177
|
+
measured: azimuths
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (geometry.detachedPartCount !== undefined && geometry.detachedPartCount > 0) {
|
|
182
|
+
checks.push({
|
|
183
|
+
id: "detached-geometry",
|
|
184
|
+
verdict: "fail",
|
|
185
|
+
detail: `${geometry.detachedPartCount} part(s) are detached from the body shell, which reads as components floating on stalks`,
|
|
186
|
+
measured: geometry.detachedPartCount
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
// Grounding confidence: an asset whose lowest point is far from its origin needs the normalization
|
|
191
|
+
// path to be exercised, and a caller should know that before trusting a placement.
|
|
192
|
+
if (geometry.minY !== undefined) {
|
|
193
|
+
const height = geometry.bounds[1];
|
|
194
|
+
const offsetRatio = height > 0 ? Math.abs(geometry.minY) / height : 0;
|
|
195
|
+
checks.push({
|
|
196
|
+
id: "grounding-offset",
|
|
197
|
+
verdict: "pass",
|
|
198
|
+
detail: offsetRatio <= 0.02
|
|
199
|
+
? "lowest point sits effectively on the asset origin"
|
|
200
|
+
: `lowest point sits ${(offsetRatio * 100).toFixed(1)}% of asset height from the origin; placement must use the normalization path rather than the raw origin`,
|
|
201
|
+
measured: Number(offsetRatio.toFixed(4))
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
/*
|
|
205
|
+
* Normalization requirement, recorded as its own check rather than folded into grounding.
|
|
206
|
+
*
|
|
207
|
+
* These are different questions and were previously conflated in one message. Grounding asks "how far is the
|
|
208
|
+
* lowest point from the origin"; normalization asks "must a caller scale this asset to a target size, or are
|
|
209
|
+
* its units already scene-appropriate". `turboRaceCar` measures 186 x 176 x 378 units against a 1.1-unit
|
|
210
|
+
* target -- a 344x fit -- and a route that skipped the normalization path would place a car the size of the
|
|
211
|
+
* circuit. Reporting it separately means a caller can see the requirement without parsing prose.
|
|
212
|
+
*/
|
|
213
|
+
{
|
|
214
|
+
const longest = Math.max(...geometry.bounds);
|
|
215
|
+
// Anything outside roughly scene-scale needs an explicit fit; 12 units is generous for a single subject.
|
|
216
|
+
const needsNormalization = longest > 12 || longest < 0.08;
|
|
217
|
+
checks.push({
|
|
218
|
+
id: "normalization-required",
|
|
219
|
+
verdict: "pass",
|
|
220
|
+
detail: needsNormalization
|
|
221
|
+
? `longest axis is ${longest.toFixed(2)} units; placement must fit to a target size rather than use raw asset units`
|
|
222
|
+
: `longest axis is ${longest.toFixed(2)} units, already scene-scale`,
|
|
223
|
+
measured: Number(longest.toFixed(4))
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
/*
|
|
227
|
+
* Orientation evidence. Never inferred: a symmetric body has no intrinsic front, so a guess here becomes a car
|
|
228
|
+
* driving backwards down the track. Absent evidence is `unproven` unless the role demands it, in which case it
|
|
229
|
+
* is a `fail` -- the brief's "reject if required orientation evidence is absent".
|
|
230
|
+
*/
|
|
231
|
+
{
|
|
232
|
+
const declared = geometry.forwardAxis;
|
|
233
|
+
const hasEvidence = Array.isArray(declared) && declared.some((component) => Math.abs(component) > 1e-6);
|
|
234
|
+
/*
|
|
235
|
+
* `not-applicable` when the role did not ask for orientation, not `unproven`.
|
|
236
|
+
*
|
|
237
|
+
* `unproven` forbids admission by design -- it is how "nobody measured whether the tyres are visible" stops
|
|
238
|
+
* being mistaken for "the tyres are visible". Reporting absent-but-unrequested orientation as `unproven`
|
|
239
|
+
* would therefore make *every* asset unadmissible, including a rock that has no meaningful forward axis. The
|
|
240
|
+
* distinction that matters is: the role asked and we cannot answer (`fail`), versus the role never asked
|
|
241
|
+
* (`not-applicable`). Both are recorded, so a reader sees the fact either way.
|
|
242
|
+
*/
|
|
243
|
+
checks.push({
|
|
244
|
+
id: "orientation-evidence",
|
|
245
|
+
verdict: hasEvidence ? "pass" : requirement.requireOrientationEvidence ? "fail" : "not-applicable",
|
|
246
|
+
detail: hasEvidence
|
|
247
|
+
? `manifest declares forward axis [${declared.join(", ")}]`
|
|
248
|
+
: requirement.requireOrientationEvidence
|
|
249
|
+
? "no manifest forward axis; orientation cannot be inferred from geometry and must not be guessed"
|
|
250
|
+
: "no manifest forward axis; this role does not require one",
|
|
251
|
+
measured: hasEvidence ? declared.join(",") : false
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
/*
|
|
255
|
+
* Front/rear inference, kept distinct from orientation evidence.
|
|
256
|
+
*
|
|
257
|
+
* Orientation says *which way* the asset faces; this says whether front and rear are even distinguishable
|
|
258
|
+
* from its geometry. A perfectly symmetric shell can carry a declared forward axis and still read ambiguously
|
|
259
|
+
* to a viewer, which is a visual-fitness fact a hero role should surface.
|
|
260
|
+
*/
|
|
261
|
+
if (geometry.frontRearDistinguishable !== undefined) {
|
|
262
|
+
checks.push({
|
|
263
|
+
id: "front-rear-inference",
|
|
264
|
+
// A reported visual-fitness fact, not a gate: `unproven` would block admission (see orientation above).
|
|
265
|
+
verdict: geometry.frontRearDistinguishable ? "pass" : "not-applicable",
|
|
266
|
+
detail: geometry.frontRearDistinguishable
|
|
267
|
+
? "front and rear are geometrically distinguishable"
|
|
268
|
+
: "front and rear are not geometrically distinguishable; a viewer may not read the facing direction",
|
|
269
|
+
measured: geometry.frontRearDistinguishable
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
/*
|
|
273
|
+
* Origin/pivot sanity. An asset whose origin sits far from its bounds centre rotates around a point outside
|
|
274
|
+
* itself, so a route that spins it appears to swing it on a boom. Distinct from grounding, which is only
|
|
275
|
+
* about the vertical axis.
|
|
276
|
+
*/
|
|
277
|
+
if (geometry.pivotOffsetRatio !== undefined) {
|
|
278
|
+
const sane = geometry.pivotOffsetRatio <= 0.35;
|
|
279
|
+
checks.push({
|
|
280
|
+
id: "origin-pivot-sanity",
|
|
281
|
+
verdict: sane ? "pass" : "fail",
|
|
282
|
+
detail: sane
|
|
283
|
+
? `origin sits ${(geometry.pivotOffsetRatio * 100).toFixed(1)}% of the longest axis from the bounds centre`
|
|
284
|
+
: `origin sits ${(geometry.pivotOffsetRatio * 100).toFixed(1)}% of the longest axis from the bounds centre; rotation will swing the asset around a point outside itself`,
|
|
285
|
+
measured: Number(geometry.pivotOffsetRatio.toFixed(4))
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
/*
|
|
289
|
+
* Material completeness, distinct from the `textured` check.
|
|
290
|
+
*
|
|
291
|
+
* `textured` asks whether the asset uses textures at all. This asks whether every mesh part has a material
|
|
292
|
+
* assigned: a part with none has no shader to draw with and renders as an untextured default or not at all,
|
|
293
|
+
* which is one of the ways a "loads successfully" asset can still be visually broken.
|
|
294
|
+
*/
|
|
295
|
+
if (geometry.partsWithoutMaterial !== undefined) {
|
|
296
|
+
const complete = geometry.partsWithoutMaterial === 0;
|
|
297
|
+
checks.push({
|
|
298
|
+
id: "material-completeness",
|
|
299
|
+
verdict: complete ? "pass" : "fail",
|
|
300
|
+
detail: complete
|
|
301
|
+
? "every mesh part carries a material assignment"
|
|
302
|
+
: `${geometry.partsWithoutMaterial} mesh part(s) carry no material assignment and have no shader to draw with`,
|
|
303
|
+
measured: geometry.partsWithoutMaterial
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
if (requirement.requireProvenance) {
|
|
307
|
+
const complete = Boolean(provenance?.license && provenance?.author);
|
|
308
|
+
checks.push({
|
|
309
|
+
id: "provenance-complete",
|
|
310
|
+
verdict: complete ? "pass" : "fail",
|
|
311
|
+
detail: complete
|
|
312
|
+
? `${provenance?.license} by ${provenance?.author}`
|
|
313
|
+
: `incomplete provenance (license: ${provenance?.license ?? "missing"}, author: ${provenance?.author ?? "missing"})`,
|
|
314
|
+
measured: complete
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
const blockers = checks
|
|
318
|
+
.filter((check) => check.verdict === "fail")
|
|
319
|
+
.map((check) => `${check.id}:${check.detail}`);
|
|
320
|
+
const unproven = checks
|
|
321
|
+
.filter((check) => check.verdict === "unproven")
|
|
322
|
+
.map((check) => `${check.id}:${check.detail}`);
|
|
323
|
+
return {
|
|
324
|
+
schema: "aura3d-asset-role-admission/1.0",
|
|
325
|
+
assetId: input.assetId,
|
|
326
|
+
role: requirement.role,
|
|
327
|
+
checks,
|
|
328
|
+
admitted: blockers.length === 0 && unproven.length === 0,
|
|
329
|
+
blockers,
|
|
330
|
+
unproven,
|
|
331
|
+
suitableAlternativeRoles: suggestAlternativeRoles(input, blockers.length > 0 || unproven.length > 0)
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Suggest roles the asset would satisfy instead.
|
|
336
|
+
*
|
|
337
|
+
* This is what makes role-aware admission useful rather than merely stricter: rejecting
|
|
338
|
+
* `showcaseCityVehicle` as a hero is correct, but it is still a good background traffic prop, and a
|
|
339
|
+
* pipeline that only says "rejected" throws away that information.
|
|
340
|
+
*/
|
|
341
|
+
function suggestAlternativeRoles(input, rejected) {
|
|
342
|
+
if (!rejected)
|
|
343
|
+
return [];
|
|
344
|
+
const roles = [];
|
|
345
|
+
const { geometry, requirement } = input;
|
|
346
|
+
const isVehicleRequest = requirement.role === "hero-vehicle" || requirement.role === "background-vehicle";
|
|
347
|
+
if (isVehicleRequest && requirement.role === "hero-vehicle" && geometry.partCount > 0) {
|
|
348
|
+
// A shell or an enclosed-wheel body still reads correctly at distance.
|
|
349
|
+
roles.push("background-vehicle");
|
|
350
|
+
}
|
|
351
|
+
if (geometry.partCount > 0 && geometry.triangles < HERO_MIN_TRIANGLES)
|
|
352
|
+
roles.push("prop");
|
|
353
|
+
return roles;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Rank candidates for a role, best first.
|
|
357
|
+
*
|
|
358
|
+
* Admitted candidates always outrank rejected ones; within each group, fewer blockers then fewer
|
|
359
|
+
* unproven checks then more triangles wins. Rejected candidates are **retained**, with their reasons,
|
|
360
|
+
* because a screening loop needs to report why every candidate failed rather than silently discarding
|
|
361
|
+
* them.
|
|
362
|
+
*/
|
|
363
|
+
export function rankAssetCandidatesForRole(inputs) {
|
|
364
|
+
return inputs
|
|
365
|
+
.map((input) => admitAssetForRole(input))
|
|
366
|
+
.sort((a, b) => {
|
|
367
|
+
if (a.admitted !== b.admitted)
|
|
368
|
+
return a.admitted ? -1 : 1;
|
|
369
|
+
if (a.blockers.length !== b.blockers.length)
|
|
370
|
+
return a.blockers.length - b.blockers.length;
|
|
371
|
+
if (a.unproven.length !== b.unproven.length)
|
|
372
|
+
return a.unproven.length - b.unproven.length;
|
|
373
|
+
const aTriangles = inputs.find((input) => input.assetId === a.assetId)?.geometry.triangles ?? 0;
|
|
374
|
+
const bTriangles = inputs.find((input) => input.assetId === b.assetId)?.geometry.triangles ?? 0;
|
|
375
|
+
return bTriangles - aTriangles;
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
function fmt(value) {
|
|
379
|
+
return value === undefined ? "unknown" : value.toFixed(2);
|
|
380
|
+
}
|
|
381
|
+
//# sourceMappingURL=asset-role-admission.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"asset-role-admission.js","sourceRoot":"","sources":["../../../../packages/aura3d-cli/src/asset-role-admission.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Real bindings for the screening pipeline's injected effects.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this file exists separately from the pipeline
|
|
5
|
+
*
|
|
6
|
+
* `asset-screening-pipeline.ts` owns *ordering and record-keeping* and takes every external effect as an
|
|
7
|
+
* injected function. That separation is what makes the part that historically failed -- running the steps
|
|
8
|
+
* in order and keeping every rejection reason -- testable offline. But an orchestrator with no real
|
|
9
|
+
* bindings is a library nobody can run: the previous state of this work had 38 passing tests against
|
|
10
|
+
* injected fakes and no way to actually screen a candidate.
|
|
11
|
+
*
|
|
12
|
+
* This module supplies the production wiring: catalog search, download, and structural geometry
|
|
13
|
+
* inspection read from the pulled file. It deliberately does **not** supply a render probe by default.
|
|
14
|
+
*
|
|
15
|
+
* ## Why no default render probe
|
|
16
|
+
*
|
|
17
|
+
* Rendering requires a browser (Playwright) and a dev server. Importing that here would make the CLI
|
|
18
|
+
* package depend on the test harness, and worse, it would tempt a caller into fabricating a "rendered"
|
|
19
|
+
* verdict from geometry when no browser is present. Omitting `renderProbe` means admission reports
|
|
20
|
+
* rendered visibility as `unproven` rather than passing -- the honest outcome, and the one the whole
|
|
21
|
+
* investigation was about. A caller with a working harness injects it explicitly.
|
|
22
|
+
*/
|
|
23
|
+
import type { AssetGeometryFacts } from "./asset-role-admission.js";
|
|
24
|
+
import type { ScreeningEffects, ScreeningRenderCost } from "./asset-screening-pipeline.js";
|
|
25
|
+
export interface ScreeningEffectsOptions {
|
|
26
|
+
/** Repository/project root, used for durable staging when registering. */
|
|
27
|
+
readonly projectDir?: string | undefined;
|
|
28
|
+
/** Directory pulled candidates are downloaded into. Defaults to a fresh temp directory. */
|
|
29
|
+
readonly stagingDir?: string | undefined;
|
|
30
|
+
readonly env?: NodeJS.ProcessEnv | undefined;
|
|
31
|
+
/** Candidates requested per query. */
|
|
32
|
+
readonly searchLimit?: number | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Optional render probe. Supply one only when a real browser harness is available.
|
|
35
|
+
*
|
|
36
|
+
* When omitted, admission correctly reports rendered visibility as `unproven`.
|
|
37
|
+
*
|
|
38
|
+
* This is a capability boundary, not a gap: geometry must never stand in for a rendered verdict.
|
|
39
|
+
*/
|
|
40
|
+
readonly renderProbe?: ScreeningEffects["renderProbe"] | undefined;
|
|
41
|
+
/** Optional registration step, so a screening run can end in a typed asset. */
|
|
42
|
+
readonly register?: ScreeningEffects["register"] | undefined;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Build production screening effects.
|
|
46
|
+
*
|
|
47
|
+
* Search failures propagate as thrown errors so the pipeline records them per query rather than treating
|
|
48
|
+
* an outage as "no candidates exist" -- a distinction that cost a whole pass when I concluded the catalog
|
|
49
|
+
* was empty without having searched it.
|
|
50
|
+
*/
|
|
51
|
+
export declare function createScreeningEffects(options?: ScreeningEffectsOptions): ScreeningEffects;
|
|
52
|
+
/**
|
|
53
|
+
* Structural geometry facts read directly from a GLB.
|
|
54
|
+
*
|
|
55
|
+
* This is the same measurement the standalone auditor performs, expressed in the admission vocabulary. It
|
|
56
|
+
* reads accessor min/max composed through the node hierarchy rather than trusting any manifest metadata,
|
|
57
|
+
* because the whole point of screening is to judge a file that is not registered yet.
|
|
58
|
+
*/
|
|
59
|
+
export declare function inspectGlbGeometry(file: string): AssetGeometryFacts & ScreeningRenderCost;
|
|
60
|
+
/** sha256 of a staged file, for provenance binding in a screening report. */
|
|
61
|
+
export declare function hashStagedFile(file: string): string;
|
|
62
|
+
/** Resolve a project-relative path, for callers staging into the repository. */
|
|
63
|
+
export declare function resolveProjectPath(projectDir: string | undefined, relativePath: string): string;
|
|
64
|
+
/**
|
|
65
|
+
* Build a render probe that reads a *retained* multi-angle visibility report.
|
|
66
|
+
*
|
|
67
|
+
* ## Why reading retained evidence is the right binding
|
|
68
|
+
*
|
|
69
|
+
* The alternative — launching a browser from inside the CLI — would make this package depend on the test
|
|
70
|
+
* harness and would put a 30-second render inside a search loop. Worse, a caller without a browser would be
|
|
71
|
+
* tempted to synthesise a verdict from geometry, which is the conflation that produced a false renderer
|
|
72
|
+
* diagnosis in the first place.
|
|
73
|
+
*
|
|
74
|
+
* Retained reports are produced by `tests/browser/vehicle-wheel-visibility.spec.ts`, which renders an asset
|
|
75
|
+
* across several azimuths and measures the lower-silhouette wheel band per angle. Reading them means the
|
|
76
|
+
* pipeline consumes *real rendered proof* while the rendering stays owned by the browser suite.
|
|
77
|
+
*
|
|
78
|
+
* A missing report is not treated as a failure: `renderedWheelVisibility` is left undefined so admission
|
|
79
|
+
* reports `unproven`. That distinction matters — "no evidence" and "evidence of absence" are different, and
|
|
80
|
+
* conflating them is what made a correctly-drawing asset look broken.
|
|
81
|
+
*/
|
|
82
|
+
export declare function createRetainedRenderProbe(options?: {
|
|
83
|
+
/** Directory holding `<assetId>.json` visibility reports. */
|
|
84
|
+
readonly reportDir?: string | undefined;
|
|
85
|
+
readonly projectDir?: string | undefined;
|
|
86
|
+
/**
|
|
87
|
+
* Minimum band pixels for a wheel band to count as readable at an angle.
|
|
88
|
+
*
|
|
89
|
+
* Measured against the accepted hero: its readable angles carry >10k band pixels while a dead-on front
|
|
90
|
+
* view carries mass that is body, not tyre. The threshold is deliberately low and paired with the
|
|
91
|
+
* outer-third test below rather than being tuned to a single asset.
|
|
92
|
+
*/
|
|
93
|
+
readonly minBandPixels?: number | undefined;
|
|
94
|
+
}): NonNullable<ScreeningEffects["renderProbe"]>;
|
|
95
|
+
//# sourceMappingURL=asset-screening-effects.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"asset-screening-effects.d.ts","sourceRoot":"","sources":["../../../../packages/aura3d-cli/src/asset-screening-effects.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAOH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,KAAK,EAEV,gBAAgB,EAEhB,mBAAmB,EACpB,MAAM,+BAA+B,CAAC;AAEvC,MAAM,WAAW,uBAAuB;IACtC,0EAA0E;IAC1E,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,2FAA2F;IAC3F,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,GAAG,SAAS,CAAC;IAC7C,sCAAsC;IACtC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C;;;;;;OAMG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,gBAAgB,CAAC,aAAa,CAAC,GAAG,SAAS,CAAC;IACnE,+EAA+E;IAC/E,QAAQ,CAAC,QAAQ,CAAC,EAAE,gBAAgB,CAAC,UAAU,CAAC,GAAG,SAAS,CAAC;CAC9D;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,uBAA4B,GAAG,gBAAgB,CAwC9F;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,mBAAmB,CA2EzF;AAmJD,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,gFAAgF;AAChF,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAE/F;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,GAAE;IACjD,6DAA6D;IAC7D,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC;;;;;;OAMG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACxC,GAAG,WAAW,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAC,CA4CpD"}
|