diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/BENCHMARK.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/BENCHMARK.md index 3774721d..1778a9e1 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/BENCHMARK.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/BENCHMARK.md @@ -40,7 +40,7 @@ Tier 3 dimension rollup was not available in this report. ## Tier 1: Static Validation Summary -Tier 1 validation passed with observations. NVSkills-Eval ran 9 checks and found 12 total findings. +Tier 1 validation passed with observations. NVSkills-Eval ran 9 checks and found 10 total findings. Top findings: @@ -52,28 +52,11 @@ Top findings: ## Tier 2: Deduplication Summary -Tier 2 validation reported findings. NVSkills-Eval ran 2 checks and found 18 total findings. +Tier 2 validation reported findings. NVSkills-Eval ran 2 checks and found 17 total findings. Top findings: -- HIGH DUPLICATE/duplicate: Duplicate content found across references/usd-structure-assessment/references/apply-restructure/references/ref-remap-mode.md and references/usd-structure-assessment/references/apply-restructure/references/restructure-mode.md: - "## Output Validation" in references/usd-structure-assessment/references/apply-restructure/references/ref-remap-mode.md (lines 73-76) - vs "## Output Validation" in references/usd-structure-assessment/references/apply-restructure/references/restructure-mode.md (lines 178-182) (`references/usd-structure-assessment/references/apply-restructure/references/ref-remap-mode.md:73`) -- HIGH DUPLICATE/duplicate: Duplicate content found across references/compare-profiles/README.md and references/omniverse-authentication/README.md and references/optimization-report/README.md and references/profile-stage/README.md and references/setup-usd-performance-tuning/references/install-kit/README.md and references/setup-usd-performance-tuning/references/install-so-standalone/README.md and references/setup-usd-performance-tuning/references/install-so-via-kit/README.md and references/usd-structure-assessment/README.md and references/usd-structure-assessment/references/apply-restructure/README.md and references/usd-structure-assessment/references/instancing-readiness/README.md and references/usd-structure-assessment/references/restructure-decision/README.md and references/usd-structure-assessment/references/usd-edit-target-planner/README.md and references/usd-structure-assessment/references/usd-hierarchy-dedupe-candidates/README.md: - "## Instructions" in references/compare-profiles/README.md (lines 10-17) - vs "## Instructions" in references/omniverse-authentication/README.md (lines 10-16) - vs "## Instructions" in references/optimization-report/README.md (lines 10-21) - vs "## Instructions" in references/profile-stage/README.md (lines 10-17) - vs "## Instructions" in references/setup-usd-performance-tuning/references/install-kit/README.md (lines 10-16) - vs "## Instructions" in references/setup-usd-performance-tuning/references/install-so-standalone/README.md (lines 10-16) - vs "## Instructions" in references/setup-usd-performance-tuning/references/install-so-via-kit/README.md (lines 10-16) - vs "## Instructions" in references/usd-structure-assessment/README.md (lines 10-16) - vs "## Instructions" in references/usd-structure-assessment/references/apply-restructure/README.md (lines 10-16) - vs "## Instructions" in references/usd-structure-assessment/references/instancing-readiness/README.md (lines 10-16) - vs "## Instructions" in references/usd-structure-assessment/references/restructure-decision/README.md (lines 10-17) - vs "## Instructions" in references/usd-structure-assessment/references/usd-edit-target-planner/README.md (lines 10-16) - vs "## Instructions" in references/usd-structure-assessment/references/usd-hierarchy-dedupe-candidates/README.md (lines 10-16) (`references/compare-profiles/README.md:10`) -- HIGH DUPLICATE/duplicate: Duplicate content found across references/cad-conversion/README.md and references/compare-profiles.md and references/operations/CLASSIFICATION.md and references/operations/EXECUTION.md and references/operations/_template.md and references/operations/boxClip.md and references/operations/computeExtents.md and references/operations/countVertices.md and references/operations/decimateMeshes.md and references/operations/deduplicateGeometry.md and references/operations/deduplicateHierarchies.md and references/operations/deleteHiddenPrims.md and references/operations/deletePrims.md and references/operations/diceMeshes.md and references/operations/editStageMetrics.md and references/operations/findCoincidingGeometry.md and references/operations/findFlatHierarchies.md and references/operations/findOccludedMeshes.md and references/operations/findOverlappingMeshes.md and references/operations/fitPrimitives.md and references/operations/flattenHierarchy.md and references/operations/generateAtlasUVs.md and references/operations/generateNormals.md and references/operations/generateProjectionUVs.md and references/operations/generateScene.md and references/operations/manifoldMeshes.md and references/operations/merge.md and references/operations/mergeVertices.md and references/operations/meshCleanup.md and references/operations/optimizeMaterials.md and references/operations/optimizePrimvars.md and references/operations/optimizeSkelRoots.md and references/operations/optimizeTimeSamples.md and references/operations/organizePrototypes.md and references/operations/pivot.md and references/operations/primitivesToMeshes.md and references/operations/printStats.md and references/operations/pruneLeaves.md and references/operations/pythonScript.md and references/operations/remeshMeshes.md and references/operations/removeAttributes.md and references/operations/removePrims.md and references/operations/removeSmallGeometry.md and references/operations/removeUntypedPrims.md and references/operations/removeUnusedUVs.md and references/operations/rtxMeshCount.md and references/operations/shrinkwrap.md and references/operations/sparseMeshes.md and references/operations/splitMeshes.md and references/operations/subdivideMeshes.md and references/operations/triangulateMeshes.md and references/operations/utilityFunction.md and references/optimization-report/references/optimization-report-template.md and references/output-workspace.md and references/report-templates/README.md and references/runtime-artifact-token-budget.md and references/setup-usd-performance-tuning/references/kit-discovery.md and references/setup-usd-performance-tuning/references/runtime-context-header.md and references/setup-usd-performance-tuning/references/runtime-probe.md and references/setup-usd-performance-tuning/references/standalone-runtime.md and references/skill-map.md and references/so-run-operations/README.md and references/so-run-operations/references/batch-mode.md and references/so-run-operations/references/config-from-evidence.md and references/so-run-operations/references/invocation.md and references/so-run-operations/references/operation-safety.md and references/so-run-operations/references/pipelines.md and references/so-run-operations/references/so-create-proxy/README.md and references/so-run-operations/references/so-create-proxy/references/bounding-box-proxy-modes.md and references/so-run-operations/references/so-create-proxy/references/decimate-step-recipes.md and references/so-run-operations/references/so-create-proxy/references/decimation-tuning.md and references/so-run-operations/references/so-create-proxy/references/proxy-config-recipes.md and references/so-run-operations/references/units-and-tolerances.md and references/upstreams/usd-optimize.md and references/usd-structure-assessment/references/apply-restructure/references/hierarchy-dedupe-rewrite-tool-spec.md and references/usd-structure-assessment/references/apply-restructure/references/ref-remap-mode.md and references/usd-structure-assessment/references/apply-restructure/references/restructure-mode.md and references/usd-structure-assessment/references/asset-structure-principles.md and references/usd-structure-assessment/references/composition-audit.md and references/usd-structure-assessment/references/factory-level-structuring.md and references/usd-structure-assessment/references/instancing-readiness/references/instancing-guide.md and references/usd-structure-assessment/references/instancing-readiness/references/instancing-tradeoffs.md and references/usd-structure-assessment/references/layer-health.md and references/usd-structure-assessment/references/optimization-tradeoffs.md and references/usd-structure-assessment/references/usd-edit-target-planner/references/output-saving.md and references/usd-structure-assessment/references/usd-edit-target-planner/references/variants-payloads.md and references/usd-structure-assessment/references/usd-hierarchy-dedupe-candidates/references/instance-candidate-finder-spec.md and references/usd-validation-runner/references/so-interpret-validators/README.md and references/usd-validation-runner/references/so-interpret-validators/references/follow-up-queries.md and references/usd-validation-runner/references/so-interpret-validators/references/rule-reference.md and references/usd-validation-runner/references/so-run-validators/README.md and references/usd-validation-runner/references/so-run-validators/references/infrastructure.md and references/usd-validation-runner/references/validate-usd-asset-validator.md and references/usd-validation-runner/references/validation-scoping.md and references/workflow.md: +- HIGH DUPLICATE/duplicate: Duplicate content found across references/cad-conversion/README.md and references/compare-profiles.md and references/operations/CLASSIFICATION.md and references/operations/EXECUTION.md and references/operations/_template.md and references/operations/boxClip.md and references/operations/computeExtents.md and references/operations/countVertices.md and references/operations/decimateMeshes.md and references/operations/deduplicateGeometry.md and references/operations/deduplicateHierarchies.md and references/operations/deleteHiddenPrims.md and references/operations/deletePrims.md and references/operations/diceMeshes.md and references/operations/editStageMetrics.md and references/operations/findCoincidingGeometry.md and references/operations/findFlatHierarchies.md and references/operations/findOccludedMeshes.md and references/operations/findOverlappingMeshes.md and references/operations/fitPrimitives.md and references/operations/flattenHierarchy.md and references/operations/generateAtlasUVs.md and references/operations/generateNormals.md and references/operations/generateProjectionUVs.md and references/operations/generateScene.md and references/operations/manifoldMeshes.md and references/operations/merge.md and references/operations/mergeVertices.md and references/operations/meshCleanup.md and references/operations/optimizeMaterials.md and references/operations/optimizePrimvars.md and references/operations/optimizeSkelRoots.md and references/operations/optimizeTimeSamples.md and references/operations/organizePrototypes.md and references/operations/pivot.md and references/operations/primitivesToMeshes.md and references/operations/printStats.md and references/operations/pruneLeaves.md and references/operations/pythonScript.md and references/operations/remeshMeshes.md and references/operations/removeAttributes.md and references/operations/removePrims.md and references/operations/removeSmallGeometry.md and references/operations/removeUntypedPrims.md and references/operations/removeUnusedUVs.md and references/operations/rtxMeshCount.md and references/operations/shrinkwrap.md and references/operations/sparseMeshes.md and references/operations/splitMeshes.md and references/operations/subdivideMeshes.md and references/operations/triangulateMeshes.md and references/operations/utilityFunction.md and references/optimization-report/references/optimization-report-template.md and references/output-workspace.md and references/report-templates/README.md and references/runtime-artifact-token-budget.md and references/setup-usd-performance-tuning/references/kit-discovery.md and references/setup-usd-performance-tuning/references/runtime-context-header.md and references/setup-usd-performance-tuning/references/runtime-probe.md and references/setup-usd-performance-tuning/references/standalone-runtime.md and references/skill-map.md and references/so-run-operations/README.md and references/so-run-operations/references/batch-mode.md and references/so-run-operations/references/config-from-evidence.md and references/so-run-operations/references/invocation.md and references/so-run-operations/references/operation-safety.md and references/so-run-operations/references/pipelines.md and references/so-run-operations/references/so-create-proxy/README.md and references/so-run-operations/references/so-create-proxy/references/bounding-box-proxy-modes.md and references/so-run-operations/references/so-create-proxy/references/decimate-step-recipes.md and references/so-run-operations/references/so-create-proxy/references/decimation-tuning.md and references/so-run-operations/references/so-create-proxy/references/proxy-config-recipes.md and references/so-run-operations/references/units-and-tolerances.md and references/upstreams/usd-optimize.md and references/usd-structure-assessment/references/apply-restructure/references/hierarchy-dedupe-rewrite-tool-spec.md and references/usd-structure-assessment/references/apply-restructure/references/ref-remap-mode.md and references/usd-structure-assessment/references/apply-restructure/references/restructure-mode.md and references/usd-structure-assessment/references/asset-structure-principles.md and references/usd-structure-assessment/references/composition-audit.md and references/usd-structure-assessment/references/factory-level-structuring.md and references/usd-structure-assessment/references/instancing-readiness/references/instancing-guide.md and references/usd-structure-assessment/references/instancing-readiness/references/instancing-tradeoffs.md and references/usd-structure-assessment/references/layer-health.md and references/usd-structure-assessment/references/optimization-tradeoffs.md and references/usd-structure-assessment/references/usd-edit-target-planner/references/output-saving.md and references/usd-structure-assessment/references/usd-edit-target-planner/references/variants-payloads.md and references/usd-structure-assessment/references/usd-hierarchy-dedupe-candidates/references/instance-candidate-finder-spec.md and references/usd-validation-runner/references/so-interpret-validators/README.md and references/usd-validation-runner/references/so-interpret-validators/references/follow-up-queries.md and references/usd-validation-runner/references/so-interpret-validators/references/rule-reference.md and references/usd-validation-runner/references/so-run-validators/README.md and references/usd-validation-runner/references/so-run-validators/references/infrastructure.md and references/usd-validation-runner/references/validate-usd-asset-validator.md and references/workflow.md: "(preamble)" in references/cad-conversion/README.md (lines 1-3) vs "(preamble)" in references/compare-profiles.md (lines 1-3) vs "(preamble)" in references/operations/CLASSIFICATION.md (lines 1-3) @@ -167,12 +150,26 @@ Top findings: vs "(preamble)" in references/usd-validation-runner/references/so-run-validators/README.md (lines 1-3) vs "(preamble)" in references/usd-validation-runner/references/so-run-validators/references/infrastructure.md (lines 1-3) vs "(preamble)" in references/usd-validation-runner/references/validate-usd-asset-validator.md (lines 1-3) - vs "(preamble)" in references/usd-validation-runner/references/validation-scoping.md (lines 1-3) vs "(preamble)" in references/workflow.md (lines 1-3) (`references/cad-conversion/README.md:1`) -- HIGH DUPLICATE/duplicate: Duplicate content found across SKILL.md and references/compare-profiles/README.md and references/omniverse-authentication/README.md and references/profile-stage/README.md and references/setup-usd-performance-tuning/references/install-kit/README.md and references/setup-usd-performance-tuning/references/install-so-standalone/README.md and references/setup-usd-performance-tuning/references/install-so-via-kit/README.md and references/usd-structure-assessment/README.md and references/usd-structure-assessment/references/apply-restructure/README.md and references/usd-structure-assessment/references/instancing-readiness/README.md and references/usd-structure-assessment/references/restructure-decision/README.md and references/usd-structure-assessment/references/usd-edit-target-planner/README.md and references/usd-structure-assessment/references/usd-hierarchy-dedupe-candidates/README.md and references/usd-validation-runner/README.md and references/usd-validation-runner/references/so-run-validators/README.md: - "## Instructions" in SKILL.md (lines 10-19) - vs "## Output Format" in SKILL.md (lines 20-26) - vs "## Output Format" in references/compare-profiles/README.md (lines 26-33) +- HIGH DUPLICATE/duplicate: Duplicate content found across references/usd-structure-assessment/references/apply-restructure/references/ref-remap-mode.md and references/usd-structure-assessment/references/apply-restructure/references/restructure-mode.md: + "## Output Validation" in references/usd-structure-assessment/references/apply-restructure/references/ref-remap-mode.md (lines 73-76) + vs "## Output Validation" in references/usd-structure-assessment/references/apply-restructure/references/restructure-mode.md (lines 179-183) (`references/usd-structure-assessment/references/apply-restructure/references/ref-remap-mode.md:73`) +- HIGH DUPLICATE/duplicate: Duplicate content found across references/compare-profiles/README.md and references/omniverse-authentication/README.md and references/optimization-report/README.md and references/profile-stage/README.md and references/setup-usd-performance-tuning/references/install-kit/README.md and references/setup-usd-performance-tuning/references/install-so-standalone/README.md and references/setup-usd-performance-tuning/references/install-so-via-kit/README.md and references/usd-structure-assessment/README.md and references/usd-structure-assessment/references/apply-restructure/README.md and references/usd-structure-assessment/references/instancing-readiness/README.md and references/usd-structure-assessment/references/restructure-decision/README.md and references/usd-structure-assessment/references/usd-edit-target-planner/README.md and references/usd-structure-assessment/references/usd-hierarchy-dedupe-candidates/README.md: + "## Instructions" in references/compare-profiles/README.md (lines 10-17) + vs "## Instructions" in references/omniverse-authentication/README.md (lines 10-16) + vs "## Instructions" in references/optimization-report/README.md (lines 10-21) + vs "## Instructions" in references/profile-stage/README.md (lines 10-17) + vs "## Instructions" in references/setup-usd-performance-tuning/references/install-kit/README.md (lines 10-16) + vs "## Instructions" in references/setup-usd-performance-tuning/references/install-so-standalone/README.md (lines 10-16) + vs "## Instructions" in references/setup-usd-performance-tuning/references/install-so-via-kit/README.md (lines 10-16) + vs "## Instructions" in references/usd-structure-assessment/README.md (lines 10-16) + vs "## Instructions" in references/usd-structure-assessment/references/apply-restructure/README.md (lines 10-16) + vs "## Instructions" in references/usd-structure-assessment/references/instancing-readiness/README.md (lines 10-16) + vs "## Instructions" in references/usd-structure-assessment/references/restructure-decision/README.md (lines 10-17) + vs "## Instructions" in references/usd-structure-assessment/references/usd-edit-target-planner/README.md (lines 10-16) + vs "## Instructions" in references/usd-structure-assessment/references/usd-hierarchy-dedupe-candidates/README.md (lines 10-16) (`references/compare-profiles/README.md:10`) +- HIGH DUPLICATE/duplicate: Duplicate content found across references/compare-profiles/README.md and references/omniverse-authentication/README.md and references/profile-stage/README.md and references/setup-usd-performance-tuning/references/install-kit/README.md and references/setup-usd-performance-tuning/references/install-so-standalone/README.md and references/setup-usd-performance-tuning/references/install-so-via-kit/README.md and references/usd-structure-assessment/README.md and references/usd-structure-assessment/references/apply-restructure/README.md and references/usd-structure-assessment/references/instancing-readiness/README.md and references/usd-structure-assessment/references/restructure-decision/README.md and references/usd-structure-assessment/references/usd-edit-target-planner/README.md and references/usd-structure-assessment/references/usd-hierarchy-dedupe-candidates/README.md and references/usd-validation-runner/references/so-run-validators/README.md: + "## Output Format" in references/compare-profiles/README.md (lines 26-33) vs "## Output Format" in references/omniverse-authentication/README.md (lines 17-20) vs "## Output Format" in references/profile-stage/README.md (lines 28-34) vs "## Output Format" in references/setup-usd-performance-tuning/references/install-kit/README.md (lines 17-20) @@ -184,8 +181,7 @@ Top findings: vs "## Output Format" in references/usd-structure-assessment/references/restructure-decision/README.md (lines 27-30) vs "## Output Format" in references/usd-structure-assessment/references/usd-edit-target-planner/README.md (lines 17-20) vs "## Output Format" in references/usd-structure-assessment/references/usd-hierarchy-dedupe-candidates/README.md (lines 17-24) - vs "## Output Format" in references/usd-validation-runner/README.md (lines 28-31) - vs "## Output Format" in references/usd-validation-runner/references/so-run-validators/README.md (lines 29-33) (`SKILL.md:10`) + vs "## Output Format" in references/usd-validation-runner/references/so-run-validators/README.md (lines 29-33) (`references/compare-profiles/README.md:26`) - HIGH DUPLICATE/duplicate: Duplicate content found across references/usd-structure-assessment/references/asset-structure-principles.md and references/usd-structure-assessment/references/instancing-readiness/references/instancing-guide.md: "#### Asset Parameterization" in references/usd-structure-assessment/references/asset-structure-principles.md (lines 259-286) vs "### By parameterization" in references/usd-structure-assessment/references/instancing-readiness/references/instancing-guide.md (lines 153-188) (`references/usd-structure-assessment/references/asset-structure-principles.md:259`) diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/SKILL.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/SKILL.md index a7b3c6d7..e7fbee88 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/SKILL.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/SKILL.md @@ -33,7 +33,7 @@ Use this workflow for broad performance asks such as slow loading, high memory, 1. Start from the mandatory runtime context gate before producing tuning output, unless the prompt is only asking for a static classification test. 2. Classify broad optimization requests as `ready_to_plan`; reserve `approval_required` for prompts that explicitly name a destructive operation to execute before planning. -3. Plan the full canonical chain through `optimization-report`, preserving the structured milestone order and the `profile-stage:baseline` / `profile-stage:after` labels when listing milestones. +3. Plan the full canonical chain through `optimization-report`, preserving the structured milestone order and the `profile-stage:baseline` / `profile-stage:after` labels when listing milestones. For broad optimization, default to 3 scoped iterations unless the user opts out, asks for a quick pass, or stop criteria apply. 4. Invoke downstream skill bodies only when their phase is reached, and keep raw runtime artifacts on disk while reading compact summaries. Frontmatter keeps `version` and `tools` at top level for agentskills.io runtime @@ -41,7 +41,7 @@ compatibility. NVCARPS discoverability fields live under `metadata`. ## Output Format -Return a plan or status summary that names the selected entry skill, uses `ready_to_plan` for generic optimization requests, includes the full milestone chain through `optimization-report`, and labels profile phases as `profile-stage:baseline` and `profile-stage:after`. For structured outputs, the broad-optimization milestone subsequence is `omniverse-usd-performance-tuning` -> `profile-stage:baseline` -> `usd-structure-assessment` -> `usd-validation-runner` -> `restructure-decision` -> `apply-restructure` -> `so-run-validators` -> `so-interpret-validators` -> `so-run-operations` -> `profile-stage:after` -> `compare-profiles` -> `optimization-report`. End-to-end execution should produce an optimized stage when mutation runs and a report conforming to the `optimization-report` reference's schema (`scripts/optimization-report.schema.json` within that reference). +Return a plan or status summary that names the selected entry skill, uses `ready_to_plan` for generic optimization requests, includes the full milestone chain through `optimization-report`, and labels profile phases as `profile-stage:baseline` and `profile-stage:after`. For structured outputs, the broad-optimization milestone subsequence is `omniverse-usd-performance-tuning` -> `profile-stage:baseline` -> `usd-structure-assessment` -> `usd-validation-runner` -> `restructure-decision` -> `apply-restructure` -> `so-run-validators` -> `so-interpret-validators` -> `so-run-operations` -> `profile-stage:after` -> `compare-profiles` -> `optimization-report`. End-to-end execution should produce an optimized stage when mutation runs and a report conforming to the `optimization-report` reference's schema (`scripts/optimization-report.schema.json` within that reference). Broad optimization should plan 3 scoped iterations by default; each iteration writes an interim report/update and later passes reuse prior evidence instead of restarting the full workflow. Use this workflow for broad performance asks such as slow loading, low FPS, high memory, GPU crashes, conversion quality, or "optimize my scene." @@ -248,7 +248,8 @@ If you have network access, prefer the live URLs (noted in each reference file) Read `references/workflow.md` for the canonical Phase 0-7 flow, including Kit/standalone branches, validator-stack routing, operation ordering, -termination conditions, duration hints, and the optional iteration pattern. +termination conditions, duration hints, and the default three-pass scoped +iteration pattern. The compact root map at `references/skill-map.md` only routes agents into this workflow. @@ -268,7 +269,7 @@ For deeper subtopic guidance, consult the references: - `references/cad-conversion/README.md` - CAD converter settings. - `references/upstreams/usd-optimize.md` - upstream SO mechanics and prebuilt package resolution. - `skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-run-validators/references/infrastructure.md` - local handoff for SO validator infrastructure. -- `skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/validation-scoping.md` - tier 1/2/3 plan + scene-aware adjustment. +- `skills/omniverse-usd-performance-tuning/references/usd-validation-runner/README.md` - tier 1/2/3 selected-probe plan, large-stage guardrails, full-sweep approval, and scene-aware adjustment. - `skills/omniverse-usd-performance-tuning/references/optimization-report/references/optimization-report-template.md` - the data contract every phase populates. For full Kit runtime profiling (FPS, frame time, Hydra/RTX metrics), refer to the external profiling skills at NVIDIA/omniperf. diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/evals/evals.json b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/evals/evals.json index 59189090..a6b24b40 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/evals/evals.json +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/evals/evals.json @@ -69,6 +69,60 @@ "Does not run profiling, validation, Scene Optimizer operations, or optimization-report steps.", "Routes to a viewer or application-building skill if one is available." ] + }, + { + "id": "usd-performance-structural-only-report", + "question": "Optimize /tmp/factory.usd, but Scene Optimizer is not installed in my runtime and I don't want to install anything right now. Still give me a report.", + "expected_skill": "omniverse-usd-performance-tuning", + "expected_script": null, + "ground_truth": "With Scene Optimizer unavailable and install declined, the workflow runs in structural-only mode: structure assessment plus pre-mutation USD validation, no mesh operations. The final report keeps verdict within its enum (neutral if nothing changed), sets workflow_mode to structural_only, and records the Scene Optimizer blocker in notes. It must not invent a structural-only verdict value.", + "expected_behavior": [ + "Reads the omniverse-usd-performance-tuning SKILL.md.", + "Recognizes Scene Optimizer is unavailable and install was declined, and continues in structural-only mode rather than halting or fabricating results.", + "Skips the Scene Optimizer mesh-operation phases.", + "Produces a report whose verdict stays within improved|neutral|regressed|mixed and sets workflow_mode to structural_only.", + "Records the Scene Optimizer blocker and the next profile capture needed in the report notes." + ] + }, + { + "id": "usd-performance-report-schema-conformance", + "question": "Finish the optimization run on /tmp/factory.usd and write the final report.", + "expected_skill": "omniverse-usd-performance-tuning", + "expected_script": null, + "ground_truth": "The final optimization report must conform to optimization-report.schema.json. The agent should validate the report JSON with python3 scripts/validate_report.py before treating it as final, generate HTML via the renderer with --fixture/--output, and must not emit out-of-enum verdicts or invented top-level fields.", + "expected_behavior": [ + "Reads the omniverse-usd-performance-tuning SKILL.md and the optimization-report reference.", + "Generates the report JSON against optimization-report.schema.json with verdict in improved|neutral|regressed|mixed.", + "Validates the finished report with python3 scripts/validate_report.py before finishing.", + "Generates the HTML via render_preview.py with --fixture and --output, not by hand and not argless.", + "Does not invent verdict values such as structural-only or no-op-needed." + ] + }, + { + "id": "usd-performance-no-overwrite-source", + "question": "Run mesh cleanup and decimation on /data/asset.usd to make it lighter.", + "expected_skill": "omniverse-usd-performance-tuning", + "expected_script": null, + "ground_truth": "Destructive Scene Optimizer operations must write a new optimized output path and must not overwrite the source asset in place. The agent should also ask for approval before the explicitly named destructive operations.", + "expected_behavior": [ + "Reads the omniverse-usd-performance-tuning SKILL.md.", + "Asks for approval before the explicitly named destructive operations (mesh cleanup, decimation).", + "Saves optimized output to a new path and does not overwrite the original source asset.", + "Validates before and after the operations and records the output path in the optimization report." + ] + }, + { + "id": "usd-performance-zero-work-operation", + "question": "I ran the optimization and the operation report says it completed - did anything actually change?", + "expected_skill": "omniverse-usd-performance-tuning", + "expected_script": null, + "ground_truth": "A Scene Optimizer operation can return success while changing nothing (zero prims affected). The agent should detect a successful-but-no-op result by comparing before/after metrics, report it honestly as neutral, and not claim an improvement the metrics do not support.", + "expected_behavior": [ + "Reads the omniverse-usd-performance-tuning SKILL.md.", + "Compares before/after metrics rather than trusting the operation's success status alone.", + "Recognizes a success-but-zero-work result (no prims or meshes changed) and reports it as neutral.", + "Does not present an unchanged stage as an improvement in the optimization report." + ] } ] } diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/compare-profiles.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/compare-profiles.md index 2a4649ff..ad8575aa 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/compare-profiles.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/compare-profiles.md @@ -36,13 +36,15 @@ workflow reference remains Report absolute values and percentages together. A neutral result is not a failure; it means the measured scene did not materially change for that metric. -## Structural-Only Verdict +## Structural-Only Runs -`verdict: structural-only` is allowed only when the run used quick structural -signals and no meaningful before/after timing or frame metrics were captured. -The report must say which runtime or access blocker prevented a stronger -performance verdict and must recommend the next profile capture needed to -graduate the verdict. +When the run used quick structural signals and no meaningful before/after timing +or frame metrics were captured, set `workflow_mode: structural_only` in the +report — do **not** invent a verdict value. The `verdict` stays within its enum +(`improved | neutral | regressed | mixed`); use `neutral` when no measured metric +materially changed. The report's `notes` field must say which runtime or access +blocker prevented a stronger performance verdict and must recommend the next +profile capture needed to graduate it. ## Terminal Report Requirement diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/compare-profiles/README.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/compare-profiles/README.md index cfda75c8..22f0b74b 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/compare-profiles/README.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/compare-profiles/README.md @@ -160,7 +160,7 @@ The full flow with profiling: omniverse-usd-performance-tuning → profile-stage (BASELINE) → usd-structure-assessment -→ usd-validation-runner (master router; uses skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/validation-scoping.md for tier detail) +→ usd-validation-runner (master router; uses skills/omniverse-usd-performance-tuning/references/usd-validation-runner/README.md for tier detail and selected-probe policy) → restructure-decision (Phase 2e gate) → instancing-readiness (if applicable) → SO operations / instancing diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/operations/EXECUTION.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/operations/EXECUTION.md index d0e86acb..5caad2dc 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/operations/EXECUTION.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/operations/EXECUTION.md @@ -19,6 +19,12 @@ plan. Detailed executable guidance lives in the nested `so-run-operations` references; this page gives repo-root agents enough shape to avoid wrong turns before entering the skill bundle. +Use setup preflight plus live `sceneOptimizer.operationsAvailable` before +execution. Per-operation files are routing stubs; upstream `usd-optimize` docs +own parameters and defaults. Local invocation mechanics live in +`../so-run-operations/references/invocation.md`; do not invent or duplicate +Python call shapes here. + ## Optional Helper Wrapper Shape Use these wrapper paths only when the selected Scene Optimizer environment or @@ -101,7 +107,10 @@ dependency-bound, or observed resource pressure makes parallelism unsafe. When targets include prototypes and non-prototype assets, run prototypes first. Parallelize within each dependency group when resources allow. Prototype changes propagate to instances, so instance-site work before prototype work -wastes runtime and can produce misleading metrics. +wastes runtime and can produce misleading metrics. If the batch manifest +contains `target_class: "assembly_root"` with retained meshes, process it as a +non-prototype mesh target before final stage-level cleanup; do not reduce it to +`pruneLeaves`/`computeExtents` only. For each target, include a stable hash of the absolute input path in optimized USD, summary, and log filenames. After every batch, verify that produced output @@ -115,3 +124,14 @@ Scene Optimizer mutates the opened stage in memory. Default to exporting an optimized `.usdc` output under `output_path`. Use in-place `Save()` only for newly created layers or explicitly approved source edits, and use flattened stage export only when the user asks for a flattened deliverable. + +## Rules + +- Confirm bounded-loss/destructive operations before mutation. +- Use selected targets from SA/validation evidence. +- Store config, log, output stage, and summary artifacts. +- If helper wrappers exist in the selected environment they may be used; + otherwise use the Python/API executor from the invocation reference. +- Do not pass a plain `pxr.Usd.Stage` directly to Scene Optimizer operation + APIs. Attach it to `ExecutionContext` or use the standalone JSON helper as + described in the invocation reference. diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/operations/_curation.json b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/operations/_curation.json index 635e2516..7055c9a3 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/operations/_curation.json +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/operations/_curation.json @@ -61,7 +61,7 @@ }, "removeUnusedUVs": { "status": "canonical", - "rationale": "canonical: C1+C2: lossless mesh-cleanup op surfaced via pipelines.md cad-bim-cleanup pipeline; routing skill points at pipelines.md rather than naming the op directly.", + "rationale": "canonical: C1+C2: lossless mesh-cleanup op surfaced as a local routing key in pipelines.md for CAD/BIM cleanup (named pipeline recipes live upstream); routing skill points at pipelines.md rather than naming the op directly.", "wired_into": [ "skills/omniverse-usd-performance-tuning/references/so-run-operations/references/pipelines.md" ] @@ -122,8 +122,11 @@ }, "findCoincidingGeometry": { "status": "analysis", - "rationale": "analysis: A1+A2: lossless coincidence analyzer; candidate for so-interpret-validators wiring.", - "wired_into": [] + "rationale": "analysis: A1+A2: lossless coincidence analyzer; wired into the so-interpret-validators interpretation map (SceneOptimizerCoincidingGeometryChecker -> spatial_coinciding) and the workflow analysis-op guidance. Prefer deduplicateGeometry before destructive deletion.", + "wired_into": [ + "skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-interpret-validators/references/rule-reference.md", + "skills/omniverse-usd-performance-tuning/references/workflow.md" + ] }, "findOccludedMeshes": { "status": "canonical", @@ -136,12 +139,15 @@ }, "findFlatHierarchies": { "status": "analysis", - "rationale": "analysis: A1+A2: lossless hierarchy-shape finder; candidate for so-interpret-validators wiring.", - "wired_into": [] + "rationale": "analysis: A1+A2: lossless hierarchy-shape finder; wired into the so-interpret-validators interpretation map (SceneOptimizerFlatHierarchiesChecker -> flattenHierarchy) and the workflow analysis-op guidance.", + "wired_into": [ + "skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-interpret-validators/references/rule-reference.md", + "skills/omniverse-usd-performance-tuning/references/workflow.md" + ] }, "fitPrimitives": { "status": "canonical", - "rationale": "canonical: C1+C2: bounded-loss geometry op invoked in cad-bim-cleanup pipeline (pipelines.md); critical for CAD/BIM/MEP scenes. Requires user confirmation (replaces meshes with analytic primitives).", + "rationale": "canonical: C1+C2: bounded-loss geometry op surfaced as a local routing key in pipelines.md for CAD/BIM cleanup (named pipeline recipes live upstream); critical for CAD/BIM/MEP scenes. Requires user confirmation (replaces meshes with analytic primitives).", "wired_into": [ "skills/omniverse-usd-performance-tuning/references/so-run-operations/references/pipelines.md", "skills/omniverse-usd-performance-tuning/references/operations/fitPrimitives.md" @@ -262,7 +268,7 @@ }, "sparseMeshes": { "status": "specialty", - "rationale": "specialty: S2: validator-finding evidence (SceneOptimizerSparseMeshChecker T2 in rule-reference.md) wires it into the so-interpret-validators chain. Analysis-only op that classifies meshes by spatial density and surfaces split / dice candidates; surfaces from validation-scoping.md Tier 3 (outlier_extent-flagged assets). Outside the default canonical flow but actionable when the checker fires.", + "rationale": "specialty: S2: validator-finding evidence (SceneOptimizerSparseMeshChecker T2 in rule-reference.md) wires it into the so-interpret-validators chain. Analysis-only op that classifies meshes by spatial density and surfaces split / dice candidates; surfaces from usd-validation-runner Tier 3 policy (outlier_extent-flagged assets). Outside the default canonical flow but actionable when the checker fires.", "wired_into": [ "skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-interpret-validators/references/rule-reference.md", "skills/omniverse-usd-performance-tuning/references/workflow.md" @@ -292,12 +298,15 @@ }, "findOverlappingMeshes": { "status": "analysis", - "rationale": "analysis: A1+A2: lossless overlap analyzer; candidate for future wiring after live operationsAvailable confirms support.", - "wired_into": [] + "rationale": "analysis: A1+A2: lossless overlap analyzer; wired into the so-interpret-validators interpretation map (SceneOptimizerFindOverlappingMeshesChecker -> spatial_overlapping) and the workflow analysis-op guidance.", + "wired_into": [ + "skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-interpret-validators/references/rule-reference.md", + "skills/omniverse-usd-performance-tuning/references/workflow.md" + ] }, "shrinkwrap": { "status": "documentary", "rationale": "documentary: D1+D2+D3: specialty geometry op; use only after live operationsAvailable confirms support.", "wired_into": [] } -} \ No newline at end of file +} diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/operations/fitPrimitives.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/operations/fitPrimitives.md index 327e622e..442fdae3 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/operations/fitPrimitives.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/operations/fitPrimitives.md @@ -9,7 +9,7 @@ requires_confirmation: true risk_class: high args_count: 20 requires_mesh: true -pipelines: [cad-bim-cleanup] +pipelines: [] keywords: [fit, primitive, cube, sphere, cylinder, approximate] since_version: 2026-02-11T07:51:19Z requires_extension: omni.scene.optimizer.core diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/optimization-report/README.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/optimization-report/README.md index 13398776..9f0af6b3 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/optimization-report/README.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/optimization-report/README.md @@ -27,8 +27,9 @@ Before generating the report, re-read and confirm: `references/report-templates/render_preview.py` with `--fixture` and `--output`. Never hand-write HTML. Never use LLM-generated HTML. - [ ] **JSON conforms to schema** — read - `scripts/optimization-report.schema.json` in full before writing. Do not - guess field names from memory. + `scripts/optimization-report.schema.json` in full before writing, then + validate the finished report with `python3 scripts/validate_report.py `. + Do not guess field names from memory. - [ ] **Runtime context copied verbatim** from `setup-preflight.json`. - [ ] **Score computed deterministically** — weighted average of metric groups, not hand-assigned. @@ -221,74 +222,134 @@ Structure: "issues": 1525, "notes": "Candidates for pruneLeaves" } - ] + ], + "target_coverage": { + "complete": true, + "source_manifests": ["apply-restructure-manifest.json"], + "entries": [ + { + "path": "/out/prototypes/discipline.usd", + "role": "prototype", + "mesh_count": 318, + "disposition": "optimized", + "operations": ["pruneLeaves", "deduplicateGeometry"] + } + ] + } } ``` -## Markdown template - -```markdown -## USD Performance Tuning Report — {asset_name} - -Stage Optimization Score: {optimization_score}/10 ({score_label}) -Verdict: {verdict} - -### Output File -{output_path} - -### Reasoning +## Phase-4 target coverage (completion gate) -{reasoning} +The required top-level `target_coverage` block is the Phase-4 analogue of the +validation report's `coverage_ledger`: it proves every mesh-optimization target +was resolved instead of silently dropped. It is structurally flat — `entries[]` +plus a `complete` boolean — to mirror the validation report rather than nesting a +`phase4` wrapper. -### Runtime Context - -| Component | Value | -|---|---| -| Kit application | {runtime_context.kit.application} {runtime_context.kit.version} | -| Kit path | {runtime_context.kit.path} | -| Kit build | {runtime_context.kit.build} | -| Scene Optimizer | {runtime_context.sceneOptimizer.extension} {runtime_context.sceneOptimizer.version} | -| Asset Validator | {runtime_context.assetValidator.package} {runtime_context.assetValidator.version} (via {runtime_context.assetValidator.source}) | - ---- - -### Before / After at a Glance - -| Metric | Before | After | Change | -|---|---|---|---| -| {for each metric} | - -### Stage Impact Areas +```json +"target_coverage": { + "complete": true, + "source_manifests": ["apply-restructure-manifest.json"], + "entries": [ + { + "path": "/out/prototypes/rack_unit.usd", + "role": "prototype", + "mesh_count": 412, + "disposition": "optimized", + "operations": ["meshCleanup", "decimateMeshes"] + }, + { + "path": "/out/assembly.usdc", + "role": "assembly_root", + "mesh_count": 0, + "disposition": "skipped_zero_meshes" + } + ] +} +``` -| Area | Score | Status | Notes | -|---|---:|---|---| -| Composition Load | {score}/10 | measured | {summary} | -| Composition Complexity | {score}/10 | measured | {summary} | -| Instancing | {score}/10 | measured | {summary} | -| Validation | {score}/10 | measured | {summary} | +- `role` is one of `assembly_root | prototype | shared_layer | loadable_subasset` + (the restructure roles) or `monolith` for a non-restructured optimize-as-is + target (N=1). +- `disposition` is one of `optimized | skipped_zero_meshes | skipped_user_declined | blocked`. + `complete` is true only when every entry is one of the first three; a `blocked` + or unresolved target keeps `complete` false and the report is not final. +- `skipped_zero_meshes` is valid only when `mesh_count == 0` (the default-predicate + count). A non-zero target cannot be skipped. +- A diagnosis-only / optimize-as-is run with no Phase-4 work is valid with + `entries: []` and `complete: true`. A `monolith`-only run records its single + target and needs no manifest. + +Because a report cannot self-attest coverage of a target it never enumerated, +reconciliation against the upstream apply-restructure manifest(s) is **mandatory +once a restructure happened**: whenever any entry has a restructure role, record +the manifest path(s) in `target_coverage.source_manifests[]` (one per iteration). +`validate_report.py` auto-loads them (resolved relative to the report) and also +accepts `--manifest`, then reconciles `target_coverage` against the **union** of +every iteration's `phase4_targets[]`: -### Runtime Profiling +```bash +python3 scripts/validate_report.py \ + [--manifest ] \ + [--manifest ...] +``` -Runtime metrics such as RAM, VRAM, FPS, frame time, shader cost, and renderer -activity are not part of the Stage Optimization Score. Use Omniperf or an -equivalent runtime profiling dashboard for those measurements. +The gate exits non-zero if a restructure report records/supplies no manifest, a +planned target is uncovered, a covered target is absent from every manifest, a +disposition is unresolved, or a `skipped_zero_meshes` target has a manifest +`mesh_count > 0`. + +## Runtime profiling handoff (producer contract) + +Runtime metrics — RAM, VRAM, FPS, frame time, Hydra sync, RTX render time, +draw-call counts, shader-compile time — are deliberately **not** inputs to the +Stage Optimization Score. They come from an external runtime profiler such as +NVIDIA/omniperf. This report is the consumer; the producer contract is: + +- The runtime profiler writes its own artifact (JSON and/or dashboard) outside + this report. Record its path in `runtime_profiling.artifact_path` and any + dashboard link in `runtime_profiling.dashboard_url`. +- Set `runtime_profiling.status` to `not_run` (no runtime profiling happened), + `external` (an artifact/dashboard exists elsewhere and is linked), or + `attached` (the profiler's summary is reproduced in `summary`). +- Put a one-line before/after runtime summary in `runtime_profiling.summary` + and any measurement caveat in `runtime_profiling.caveat`. +- Keep these values out of `metric_groups[]` and `metrics[]` so the stage score + stays composition-only while the runtime numbers remain visible in the report. ---- +## Markdown template -### Operations Performed +The Markdown summary is **generated from the report JSON**, never hand-written. +There is exactly one canonical Markdown layout: the committed template +`references/report-templates/optimization-report.md.template` (Jinja-style, +double-brace placeholders, with `{{ executive_summary }}`, a Runtime Context +table, and `{% for %}` loops over `metric_groups`, `metrics`, `operations`, and +`validators`). Its field names track `scripts/optimization-report.schema.json` +(including `executive_summary` and the top-level `notes`). -| # | Operation | Method | Result | -|---|---|---|---| -| {for each operation} | +Render it with the **same committed renderer** used for the HTML report — just +point `--template` at the Markdown template instead of the default HTML one: ---- +```bash +python3 references/report-templates/render_preview.py \ + --fixture /_optimization_report.json \ + --template references/report-templates/optimization-report.md.template \ + --output /_optimization_report.md +``` -### Validators Attempted / Result +**Do not hand-fill a Markdown report** and do not paste a template body into +chat as a fill-in form — that is the dual-maintenance drift this contract exists +to prevent, and it violates the SKILL-level "never hand-write the report" rule. +The JSON is the source of truth; the `.md.template` above is the only place the +Markdown layout is maintained. -| Validator | Result | -|---|---| -| {for each validator} | -``` +The renderer emits the following sections in order (rendered-output preview — +illustrative only, **not** a hand-fill target): the title with the asset name; +a Stage Optimization Score / verdict / generated-timestamp / output line; the +executive summary; Reasoning; a Runtime Context table; Stage Impact Areas; a +Runtime Profiling note (plus a Runtime Profiling table when handoff fields are +present); Metric Evidence; Operations; and Validators. ## Rules @@ -302,14 +363,20 @@ equivalent runtime profiling dashboard for those measurements. > canonical shape and validate your output against the `required` and `items` > blocks before emitting. The schema rejects extra keys in `metric_groups[]`, > `metrics[]`, `operations[]`, `validators[]`, and `artifacts`; update the -> schema first if a new report field is genuinely needed. Conformance failures -> are caught by `scored_static_html_report_required` in runtime checks. +> schema first if a new report field is genuinely needed. Validate the finished +> JSON by running `python3 scripts/validate_report.py ` (committed, +> dependency-free) before treating the report as final; the repository test +> `test_committed_report_fixtures_conform_to_schema` holds the bundled fixtures +> to this same schema. - Always save the JSON report — it is the structured record of what happened. - Always present the markdown to the user in chat. -- Always produce the static HTML report when writing report artifacts. In - runtime checks, include `scored_static_html_report_required` in - `phase_guardrails` when planning this final report contract. +- Always produce the static HTML report when writing report artifacts, and run + `python3 scripts/validate_report.py` on the report JSON before finishing. + `scored_static_html_report_required` is an agent-asserted planning guardrail — + list it in `phase_guardrails` when planning this final report contract — not an + automated gate. The automated conformance check is `python3 scripts/validate_report.py` + plus the repository schema test. - Always title the reader-facing report **USD Performance Tuning Report**. - Always include a dedicated `Reasoning` section with one to two concise paragraphs explaining why the selected optimizations fit the evidence. diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/optimization-report/references/optimization-report-template.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/optimization-report/references/optimization-report-template.md index ed1e80b3..afc432e5 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/optimization-report/references/optimization-report-template.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/optimization-report/references/optimization-report-template.md @@ -22,16 +22,18 @@ Required top-level fields: | `asset_name` | string | Phase 0 | Set early; the basename of the input asset usually suffices. | | `input_path` | string | Phase 0 | Optional in schema, but capture it for traceability. | | `output_path` | string | Phase 5 | Path to the optimized stage root from Phase 5d (or `null` for diagnosis-only / structural-only path). | -| `timestamp` | string (ISO 8601) | Phase 6e | Set when the report writes. | -| `verdict` | enum: `improved \| neutral \| regressed \| mixed` | Phase 6c | From `compare-profiles`. Use `structural-only` semantically when SO was unavailable (degraded path) and document it in `notes`. | -| `optimization_score` | number 0-10 | Phase 6e | Stage Optimization Score. Compute deterministically as `round(sum(group.score * group.weight) / sum(group.weight), 1)` across scored stage/composition groups only. Exclude `score=null` and `weight=0` groups. Runtime metrics are not score inputs. | -| `score_scope` | enum: `stage_optimization` | Phase 6e | Makes the score scope explicit so readers do not confuse it with full runtime performance. | -| `score_label` | enum | Phase 6e | Human score band from `optimization_score`: `excellent >= 9.0`, `strong >= 7.5`, `moderate >= 5.5`, `neutral >= 4.5`, `mixed >= 2.5`, `regressed < 2.5`. | -| `reasoning` | string | Phase 6e | One to two paragraphs explaining why the agent chose this optimization approach for the asset, based on evidence and tradeoffs. | +| `timestamp` | string (ISO 8601) | Phase 6d | Set when the report writes. | +| `verdict` | enum: `improved \| neutral \| regressed \| mixed` | Phase 6c | From `compare-profiles`. Stays in this enum in every mode; use `neutral` when no metrics changed. Express degraded/no-op runs via `workflow_mode`, not new verdict values. | +| `workflow_mode` | enum: `full \| structural_only \| no_op` | Phase 6d | Optional (default `full`). `structural_only` when SO was unavailable and only USD-structural work ran; `no_op` when SA reported `already_optimized`. | +| `notes` | string | any phase | Optional. Caveats the verdict/score cannot capture: degraded-path reason, runtime/access blocker, or the next profile capture needed to graduate the verdict. | +| `optimization_score` | number 0-10 | Phase 6d | Stage Optimization Score. Compute deterministically as `round(sum(group.score * group.weight) / sum(group.weight), 1)` across scored stage/composition groups only. Exclude `score=null` and `weight=0` groups. Runtime metrics are not score inputs. | +| `score_scope` | enum: `stage_optimization` | Phase 6d | Makes the score scope explicit so readers do not confuse it with full runtime performance. | +| `score_label` | enum | Phase 6d | Human score band from `optimization_score`: `excellent >= 9.0`, `strong >= 7.5`, `moderate >= 5.5`, `neutral >= 4.5`, `mixed >= 2.5`, `regressed < 2.5`. | +| `reasoning` | string | Phase 6d | One to two paragraphs explaining why the agent chose this optimization approach for the asset, based on evidence and tradeoffs. | | `measurement_context` | object | Phases 0, 1a, 6a | Context for stage/composition measurements: runtime, cache policy, sample count, stage-open method. | -| `runtime_profiling` | object | Phase 6e | Optional Omniperf/runtime-profiler handoff for RAM, VRAM, FPS, frame time, shader, renderer, and GPU metrics. | -| `metric_groups[]` | array | Phase 6e | Stage headline areas such as composition load, structure, instancing, storage footprint, and validation. | -| `artifacts` | object | Phase 6e | Paths to generated JSON, Markdown, and static HTML reports. | +| `runtime_profiling` | object | Phase 6d | Optional Omniperf/runtime-profiler handoff for RAM, VRAM, FPS, frame time, shader, renderer, and GPU metrics. | +| `metric_groups[]` | array | Phase 6d | Stage headline areas such as composition load, structure, instancing, storage footprint, and validation. | +| `artifacts` | object | Phase 6d | Paths to generated JSON, Markdown, and static HTML reports. | | `metrics[]` | array | Phases 1a + 6a | Each metric: `name`, `before`, `after`, `change_pct`, `verdict`. | | `operations[]` | array | Phases 4 + 5 | Each op: `order`, `name`, `method`, `result`. | | `validators[]` | array | Phases 2c + 6b | Each validator entry: `name`, `issues`, `notes`; `issues` is the count of reported findings for that row. | @@ -64,8 +66,8 @@ in `runtime_profiling`, ideally via Omniperf dashboard/artifacts. ### Phase 2 - Composition / discovery / restructure decision -- [ ] `validators[]` - first entries (validator name + issue count from Phase 2c sweep). One row per validator that ran. -- [ ] If user takes the "exit" branch at Phase 2e gate: skip to Phase 6e and write a structural-only report. +- [ ] `validators[]` - first entries (validator name + issue count from Phase 2c selected probes). One row per validator that ran. +- [ ] If user takes the "exit" branch at Phase 2e gate: skip to Phase 6d and write a diagnosis-only report (`output_path: null`, empty `operations[]`). ### Phase 3 - Stage-level instancing @@ -96,7 +98,7 @@ in `runtime_profiling`, ideally via Omniperf dashboard/artifacts. - [ ] `reasoning` - one to two concise paragraphs explaining the chosen optimization strategy and tradeoffs. - [ ] `runtime_profiling` - point to Omniperf/runtime-profiler artifacts if available, or mark as `not_run` with a recommendation. - [ ] `artifacts` - include the JSON, Markdown, and HTML report paths. -- [ ] Generate HTML by running `python3 references/report-templates/render_preview.py` (mandatory — do NOT hand-write HTML). See `references/optimization-report/README.md § HTML Generation`. +- [ ] Generate HTML by running `python3 references/report-templates/render_preview.py --fixture --output ` (mandatory — do NOT hand-write HTML, and never run it argless: that renders the committed design fixture, not your report). See `references/optimization-report/README.md § HTML Generation`. Do not emit the final report as a normal completed optimization if Phase 6a or Phase 6b artifacts are missing. Either run the missing phase, or record the @@ -110,7 +112,7 @@ unclaimed, and keep the verdict no stronger than the remaining evidence allows. When SO is unavailable and the user declines setup: - `output_path` may be `null` (no Phase 4 ops were applied, no Phase 5 ref-rewrite happened). -- `verdict` should be set to whatever applies; if no metrics changed, `neutral` is acceptable. Note structural-only status in the `validators[]` notes or a top-level `notes` field. +- `verdict` stays in its enum; if no metrics changed, use `neutral`. Set `workflow_mode: structural_only` and record the SO-unavailable reason in the top-level `notes` field. - `operations[]` may be empty. - `validators[]` should still contain the Phase 2c USD-stack findings. - `runtime_profiling.status` should usually be `not_run` unless an external Omniperf/runtime-profiler artifact is attached. @@ -147,11 +149,16 @@ the caveat above attached. ### Iteration (Phase 7) -When the agent loops back from Phase 7 (optional): +When the agent loops back from Phase 7: +- Default broad optimization to 3 scoped iterations unless the user opts out, + requests a quick pass, or stop criteria apply. +- Write an interim report/update after each iteration before continuing. - KEEP the `before` values from the FIRST baseline. Do NOT re-baseline. - Append to `operations[]` with continued `order` numbering across iterations. - Update `after` values from each new Phase 6a re-profile. +- Reuse prior validator evidence unless the next pass needs a narrower targeted + or delta probe; expanded validation scope requires explicit approval. - The final `verdict` reflects the cumulative comparison (first baseline vs latest after). ### Diagnosis-only diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/optimization-report/scripts/optimization-report.schema.json b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/optimization-report/scripts/optimization-report.schema.json index 544d3b71..34f2c8e7 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/optimization-report/scripts/optimization-report.schema.json +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/optimization-report/scripts/optimization-report.schema.json @@ -18,8 +18,10 @@ "metric_groups", "metrics", "operations", - "validators" + "validators", + "target_coverage" ], + "additionalProperties": false, "properties": { "asset_name": { "type": "string", @@ -30,8 +32,8 @@ "description": "Path to the original input stage." }, "output_path": { - "type": "string", - "description": "Path to the optimized output stage." + "type": ["string", "null"], + "description": "Path to the optimized output stage; null for diagnosis-only, no-op, or structural-only runs where no optimized stage was written." }, "timestamp": { "type": "string", @@ -41,7 +43,16 @@ "verdict": { "type": "string", "enum": ["improved", "neutral", "regressed", "mixed"], - "description": "Overall optimization verdict." + "description": "Overall optimization verdict from compare-profiles. Stays within this enum regardless of workflow_mode; use 'neutral' when no metrics changed. Degraded (Scene Optimizer unavailable) and no-op runs are expressed via workflow_mode, not by inventing new verdict values." + }, + "workflow_mode": { + "type": "string", + "enum": ["full", "structural_only", "no_op"], + "description": "Execution mode for this run. 'full' = optimization/mutation ran (default when omitted); 'structural_only' = Scene Optimizer was unavailable so only USD-structural work ran and no mesh operations executed; 'no_op' = no optimization was needed (e.g., structure assessment reported already_optimized). The verdict stays in its own enum in every mode." + }, + "notes": { + "type": "string", + "description": "Free-form caveats the verdict and score cannot capture on their own: degraded-path explanation, the runtime or access blocker that prevented a stronger verdict, or the next profile capture needed to graduate it." }, "runtime_context": { "type": "object", @@ -121,6 +132,10 @@ "enum": ["excellent", "strong", "moderate", "neutral", "mixed", "regressed"], "description": "Human-readable score band derived from optimization_score: excellent >= 9.0; strong >= 7.5 and < 9.0; moderate >= 5.5 and < 7.5; neutral >= 4.5 and < 5.5; mixed >= 2.5 and < 4.5; regressed < 2.5." }, + "executive_summary": { + "type": "string", + "description": "Optional short reader-facing summary rendered near the top of the Markdown/HTML report." + }, "reasoning": { "type": "string", "description": "One to two concise paragraphs explaining why the agent chose the specific stage optimization approach for this asset, including the evidence that drove the choice and any tradeoffs." @@ -252,6 +267,58 @@ "notes": { "type": "string" } } } + }, + "target_coverage": { + "type": "object", + "description": "Phase-4 mesh-optimization coverage ledger, structurally parallel to the validation report's coverage_ledger. entries[] must cover the UNION of every iteration's apply-restructure manifest phase4_targets[]; complete is true only when every entry reached a resolved disposition. validate_report.py reconciles this against the upstream manifest(s) so a target that was never enumerated (e.g. an assembly_root dropped from a later iteration's manifest) fails closed instead of silently passing. Reconciliation is NOT optional once a restructure happened: if any entry has a restructure role (assembly_root | prototype | shared_layer | loadable_subasset) the gate requires a manifest, supplied via --manifest or recorded in source_manifests[]. A diagnosis-only / optimize-as-is-monolith run (no restructure roles) is manifest-free; an empty Phase-4 run is valid with entries: [] and complete: true.", + "required": ["complete", "entries"], + "additionalProperties": false, + "properties": { + "complete": { + "type": "boolean", + "description": "True only when every entry's disposition is one of optimized | skipped_zero_meshes | skipped_user_declined. A 'blocked' or unresolved entry keeps this false and the report is not final." + }, + "source_manifests": { + "type": "array", + "items": { "type": "string" }, + "description": "Path(s) to the apply-restructure manifest(s) this coverage was reconciled against, one per restructure iteration. Recorded so validate_report.py can auto-load and reconcile them (resolved relative to the report file when not absolute), making reconciliation fail-closed rather than dependent on the operator remembering --manifest. Required in effect whenever any entry has a restructure role; omit for monolith/diagnosis runs." + }, + "entries": { + "type": "array", + "items": { + "type": "object", + "required": ["path", "role", "mesh_count", "disposition"], + "additionalProperties": false, + "properties": { + "path": { + "type": "string", + "description": "Phase-4 target file; reconciliation key against apply-restructure manifest phase4_targets[].path." + }, + "role": { + "type": "string", + "enum": ["assembly_root", "prototype", "shared_layer", "loadable_subasset", "monolith"], + "description": "Target kind. The restructure roles (assembly_root | prototype | shared_layer | loadable_subasset) trigger mandatory manifest reconciliation. 'monolith' is the non-restructured optimize-as-is target (N=1) and does not require a manifest." + }, + "mesh_count": { + "type": "integer", + "minimum": 0, + "description": "Default-predicate mesh count for this target (echoed from the manifest's authoritative count). disposition 'skipped_zero_meshes' is valid only when this is 0." + }, + "disposition": { + "type": "string", + "enum": ["optimized", "skipped_zero_meshes", "skipped_user_declined", "blocked"], + "description": "optimized = the per-target mesh op chain ran; skipped_zero_meshes = no meshes to optimize (requires mesh_count == 0); skipped_user_declined = user opted out of optimizing this target; blocked = could not be processed (keeps complete=false)." + }, + "operations": { + "type": "array", + "items": { "type": "string" }, + "description": "Optional: the mesh op chain applied to this target, for traceability." + }, + "notes": { "type": "string" } + } + } + } + } } } } diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/optimization-report/scripts/validate_report.py b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/optimization-report/scripts/validate_report.py new file mode 100644 index 00000000..ce7eb1f8 --- /dev/null +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/optimization-report/scripts/validate_report.py @@ -0,0 +1,338 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Validate a USD Performance Tuning report against optimization-report.schema.json. + +Deterministic local validation with no third-party runtime dependencies. The +agent (or CI) should run this before treating a report as final, so an +out-of-enum verdict, a missing required field, or an unexpected array-item key +is caught instead of shipping a schema-invalid report. + +Implements the JSON Schema draft-07 subset this schema uses: type (including +type unions like ["string", "null"]), enum, required, properties, +additionalProperties=false, items, minimum, and maximum. + +Phase-4 target coverage gate +---------------------------- +Schema validation alone cannot catch a Phase-4 target that was never enumerated +in the report (the failure mode where an assembly_root remainder is silently +left un-optimized). A report's ``target_coverage.complete`` flag is self-attested +by the report author, so the gate reconciles ``target_coverage`` against the +upstream apply-restructure manifest(s): the report must cover the UNION of every +iteration's ``phase4_targets[]``, every disposition must be resolved, and +``skipped_zero_meshes`` is accepted only when the manifest's authoritative +``mesh_count`` for that target is 0. + +Reconciliation is fail-closed, not opt-in: when any coverage entry has a +restructure role (assembly_root | prototype | shared_layer | loadable_subasset) +a manifest is REQUIRED. Manifests are taken from ``--manifest`` and/or the +report's own ``target_coverage.source_manifests[]`` (auto-loaded relative to the +report), so a restructure report cannot pass merely because the operator forgot +the flag. Monolith/diagnosis runs (no restructure roles) stay manifest-free. + +Usage: + python3 validate_report.py [--schema ] \\ + [--manifest ...] +Exit code 0 when the report conforms and the coverage gate passes, 1 otherwise. +""" +from __future__ import annotations + +import argparse +import json +from pathlib import Path +from typing import Any + +DEFAULT_SCHEMA = Path(__file__).resolve().parent / "optimization-report.schema.json" + +#: A Phase-4 target is "resolved" only with one of these dispositions. ``blocked`` +#: (or a target with no entry at all) keeps ``target_coverage.complete`` false and +#: the report non-final — mirroring the validation report's RESOLVED_STATUSES. +PHASE4_RESOLVED_DISPOSITIONS = frozenset( + {"optimized", "skipped_zero_meshes", "skipped_user_declined"} +) +RESTRUCTURE_TARGET_CLASSES = frozenset( + {"prototype", "shared_layer", "loadable_subasset", "assembly_root"} +) +#: Coverage-entry roles that mean "a restructure happened", so a manifest is +#: mandatory and reconciliation is not optional. The ``monolith`` role (an +#: optimize-as-is N=1 target) and an empty ledger stay manifest-free. +RESTRUCTURE_ROLES = frozenset( + {"assembly_root", "prototype", "shared_layer", "loadable_subasset"} +) + + +def _type_ok(instance: Any, type_name: str) -> bool: + if type_name == "object": + return isinstance(instance, dict) + if type_name == "array": + return isinstance(instance, list) + if type_name == "string": + return isinstance(instance, str) + if type_name == "number": + return isinstance(instance, (int, float)) and not isinstance(instance, bool) + if type_name == "integer": + return isinstance(instance, int) and not isinstance(instance, bool) + if type_name == "boolean": + return isinstance(instance, bool) + if type_name == "null": + return instance is None + return True + + +def _validate(instance: Any, schema: dict, path: str, errors: list[str]) -> None: + declared_type = schema.get("type") + if declared_type is not None: + candidates = declared_type if isinstance(declared_type, list) else [declared_type] + if not any(_type_ok(instance, name) for name in candidates): + got = "null" if instance is None else type(instance).__name__ + errors.append(f"{path}: expected type {candidates}, got {got}") + return + + if "enum" in schema and instance not in schema["enum"]: + errors.append(f"{path}: {instance!r} is not one of {schema['enum']}") + + if isinstance(instance, (int, float)) and not isinstance(instance, bool): + if "minimum" in schema and instance < schema["minimum"]: + errors.append(f"{path}: {instance} is below minimum {schema['minimum']}") + if "maximum" in schema and instance > schema["maximum"]: + errors.append(f"{path}: {instance} is above maximum {schema['maximum']}") + + if isinstance(instance, dict): + properties = schema.get("properties", {}) + for required_key in schema.get("required", []): + if required_key not in instance: + errors.append(f"{path}: missing required property '{required_key}'") + allow_additional = schema.get("additionalProperties", True) + for key, value in instance.items(): + if key in properties: + _validate(value, properties[key], f"{path}.{key}", errors) + elif allow_additional is False: + errors.append(f"{path}: unexpected property '{key}'") + + if isinstance(instance, list) and "items" in schema: + for index, item in enumerate(instance): + _validate(item, schema["items"], f"{path}[{index}]", errors) + + +def validate_report(report: Any, schema: dict | None = None) -> list[str]: + """Return a list of schema-violation messages; empty list means the report conforms.""" + if schema is None: + schema = json.loads(DEFAULT_SCHEMA.read_text(encoding="utf-8")) + errors: list[str] = [] + _validate(report, schema, "$", errors) + return errors + + +def validate_manifest_structure(manifest: Any) -> list[str]: + """Enforce the load-bearing apply-restructure manifest invariants. + + Independent of the JSON-Schema walker so the rules hold without ``jsonschema``: + a ``mode=restructure`` manifest must carry a non-empty ``phase4_targets[]``, + and every target must declare an integer ``mesh_count >= 0`` (the authoritative + default-predicate count the coverage gate keys on). + """ + errors: list[str] = [] + if not isinstance(manifest, dict): + return [f"manifest must be an object, got {type(manifest).__name__}"] + mode = manifest.get("mode") + targets = manifest.get("phase4_targets") + if mode == "restructure" and not targets: + errors.append( + "mode=restructure manifest must list a non-empty phase4_targets[] " + "(do not drop the key; an assembly_root with retained meshes must appear)" + ) + for index, target in enumerate(targets or []): + where = f"phase4_targets[{index}]" + path = target.get("path") if isinstance(target, dict) else None + label = f"{where} ({path})" if path else where + if not isinstance(target, dict): + errors.append(f"{where}: must be an object") + continue + if not isinstance(path, str) or not path: + errors.append(f"{where}: missing required 'path'") + target_class = target.get("target_class") + if target_class not in RESTRUCTURE_TARGET_CLASSES: + errors.append( + f"{label}: target_class {target_class!r} not in {sorted(RESTRUCTURE_TARGET_CLASSES)}" + ) + mesh_count = target.get("mesh_count") + if isinstance(mesh_count, bool) or not isinstance(mesh_count, int) or mesh_count < 0: + errors.append( + f"{label}: mesh_count must be an integer >= 0 (authoritative " + f"default-predicate count), got {mesh_count!r}" + ) + return errors + + +def load_recorded_manifests( + report: Any, base_dir: Path +) -> tuple[list[tuple[str, Any]], list[str]]: + """Load the manifests recorded in ``target_coverage.source_manifests[]``. + + Relative paths resolve against ``base_dir`` (the report's directory) so a + report can carry its own provenance and the gate fails closed without the + operator having to remember ``--manifest``. Returns ``(labeled_manifests, + errors)`` where each labeled manifest is ``(source_path, manifest_dict)``. + """ + labeled: list[tuple[str, Any]] = [] + errors: list[str] = [] + coverage = report.get("target_coverage") if isinstance(report, dict) else None + if not isinstance(coverage, dict): + return labeled, errors + for rel in coverage.get("source_manifests", []) or []: + path = Path(rel) + if not path.is_absolute(): + path = base_dir / path + try: + labeled.append((rel, json.loads(path.read_text(encoding="utf-8")))) + except (OSError, json.JSONDecodeError) as exc: + errors.append( + f"target_coverage.source_manifests entry {rel!r} could not be loaded: {exc}" + ) + return labeled, errors + + +def _manifest_targets(manifests: list[Any]) -> dict[str, int | None]: + """Union of every manifest's phase4_targets path -> authoritative mesh_count. + + Multi-iteration runs must reconcile against the UNION: the exact regression + that prompted this gate was iteration 1 listing an assembly_root that + iteration 2's manifest dropped, leaving it uncovered by the final report. + """ + planned: dict[str, int | None] = {} + for manifest in manifests: + for target in manifest.get("phase4_targets", []) or []: + if isinstance(target, dict) and isinstance(target.get("path"), str): + planned[target["path"]] = target.get("mesh_count") + return planned + + +def reconcile_target_coverage(report: Any, manifests: list[Any] | None = None) -> list[str]: + """Gate the report's Phase-4 target_coverage; reconcile against manifest(s). + + Returns violation messages (empty == the gate passes). Always checks the + report's internal consistency (resolved dispositions, the + ``skipped_zero_meshes => mesh_count == 0`` rule, and the ``complete`` flag). + When ``manifests`` are supplied it also asserts the covered set equals the + union of every manifest's ``phase4_targets[]`` and cross-checks each + disposition against the manifest's authoritative ``mesh_count``. + """ + errors: list[str] = [] + coverage = report.get("target_coverage") if isinstance(report, dict) else None + if not isinstance(coverage, dict): + return ["target_coverage missing or not an object"] + entries = coverage.get("entries", []) + by_path: dict[str, dict[str, Any]] = {} + for entry in entries: + if isinstance(entry, dict) and isinstance(entry.get("path"), str): + by_path[entry["path"]] = entry + + for entry in entries: + path = entry.get("path", "") + disposition = entry.get("disposition") + mesh_count = entry.get("mesh_count") + if disposition == "skipped_zero_meshes" and mesh_count != 0: + errors.append( + f"target_coverage entry {path}: skipped_zero_meshes requires " + f"mesh_count == 0, got {mesh_count!r} (a non-zero target cannot be skipped)" + ) + + present_restructure_roles = sorted( + {e.get("role") for e in entries} & RESTRUCTURE_ROLES + ) + if present_restructure_roles and not manifests: + errors.append( + "target_coverage has restructure role(s) " + f"{present_restructure_roles} but no source manifest was supplied or recorded; " + "reconciliation is mandatory once a restructure happened. Record " + "target_coverage.source_manifests[] (or pass --manifest) so the covered set is " + "reconciled against the planned phase4_targets[] instead of self-attested." + ) + + all_resolved = all( + e.get("disposition") in PHASE4_RESOLVED_DISPOSITIONS for e in entries + ) + if coverage.get("complete") is not True: + errors.append( + "target_coverage.complete must be true for a final report " + "(false => a Phase-4 target is unresolved/blocked)" + ) + elif not all_resolved: + errors.append( + "target_coverage.complete is true but some entries are unresolved " + "(only optimized | skipped_zero_meshes | skipped_user_declined count as resolved)" + ) + + if manifests: + planned = _manifest_targets(manifests) + planned_paths = set(planned) + covered_paths = set(by_path) + for path in sorted(planned_paths - covered_paths): + errors.append( + f"target_coverage is missing an entry for manifest phase4_target: {path} " + "(every planned Phase-4 target, across all iterations, must be covered)" + ) + for path in sorted(covered_paths - planned_paths): + errors.append( + f"target_coverage entry {path} is not present in any supplied manifest " + "phase4_targets[] (unexpected target or a missing manifest)" + ) + for path in sorted(planned_paths & covered_paths): + authoritative = planned[path] + disposition = by_path[path].get("disposition") + if ( + disposition == "skipped_zero_meshes" + and isinstance(authoritative, int) + and authoritative > 0 + ): + errors.append( + f"target_coverage entry {path}: skipped_zero_meshes but the manifest's " + f"authoritative mesh_count is {authoritative} > 0 (lying skip)" + ) + return errors + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("report", type=Path, help="Path to the report JSON to validate.") + parser.add_argument("--schema", type=Path, default=DEFAULT_SCHEMA) + parser.add_argument( + "--manifest", + type=Path, + action="append", + default=[], + help="apply-restructure manifest(s) to reconcile Phase-4 coverage against; " + "repeat once per iteration so the union is checked. Manifests recorded in " + "the report's target_coverage.source_manifests[] are loaded automatically " + "and merged with these.", + ) + args = parser.parse_args() + + report = json.loads(args.report.read_text(encoding="utf-8")) + schema = json.loads(args.schema.read_text(encoding="utf-8")) + errors = validate_report(report, schema) + + labeled: list[tuple[str, Any]] = [] + for manifest_path in args.manifest: + labeled.append((manifest_path.name, json.loads(manifest_path.read_text(encoding="utf-8")))) + recorded, load_errors = load_recorded_manifests(report, args.report.resolve().parent) + errors.extend(load_errors) + labeled.extend(recorded) + + for label, manifest in labeled: + errors.extend(f"{label}: {msg}" for msg in validate_manifest_structure(manifest)) + + manifests = [manifest for _, manifest in labeled] + errors.extend(reconcile_target_coverage(report, manifests)) + + if errors: + print(f"{args.report}: INVALID ({len(errors)} error(s))") + for error in errors: + print(f" {error}") + return 1 + print(f"{args.report}: OK") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/output-workspace.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/output-workspace.md index c88815aa..425d920c 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/output-workspace.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/output-workspace.md @@ -50,11 +50,11 @@ probe. ```text ─── Runtime context ─────────────────────────────────────────────────────── -Kit application: {kit.chosen.application} {kit.chosen.version} - path: {kit.chosen.path} - build: {kit.chosen.build} -Scene Optimizer: {sceneOptimizer.extension} {sceneOptimizer.version} -Asset Validator: {assetValidator.package} {assetValidator.version} via {assetValidator.source} +Kit application: {runtime_context.kit.application} {runtime_context.kit.version} + path: {runtime_context.kit.path} + build: {runtime_context.kit.build} +Scene Optimizer: {runtime_context.sceneOptimizer.extension} {runtime_context.sceneOptimizer.version} +Asset Validator: {runtime_context.assetValidator.package} {runtime_context.assetValidator.version} via {runtime_context.assetValidator.source} ─────────────────────────────────────────────────────────────────────────── ``` diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/profile-stage/README.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/profile-stage/README.md index 5690ddee..fada6985 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/profile-stage/README.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/profile-stage/README.md @@ -223,7 +223,7 @@ quick mode plus: ### Prerequisites - Isaac Sim or Kit SDK with RTX renderer. -- Scene Optimizer `omni.kit.profiler.tracy` extension. +- Kit `omni.kit.profiler.tracy` profiler extension (Tracy is a Kit profiler, not a Scene Optimizer component). - GPU with display (headless with virtual display works). ### Usage @@ -346,7 +346,7 @@ When this reference ran in quick mode only, **the report's `verdict` should explicitly note that render-time claims (FPS, frame time, VRAM, draw-call count) are unmeasured**. Improvements predicted by `rtxMeshCount` or prototype sharing are plausible but not verified. See -`skills/omniverse-usd-performance-tuning/references/optimization-report/references/optimization-report-template.md` §"Degraded path (SO +`skills/omniverse-usd-performance-tuning/references/optimization-report/references/optimization-report-template.md` §"Structural-only path (SO unavailable)" and §"Quick-mode-only caveat" for the report wording. ## Rules diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/report-templates/optimization-report.design-fixture.json b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/report-templates/optimization-report.design-fixture.json index 103da941..449580f2 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/report-templates/optimization-report.design-fixture.json +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/report-templates/optimization-report.design-fixture.json @@ -166,5 +166,26 @@ "issues": 0, "notes": "No mesh-quality issues found in the sampled path." } - ] + ], + "target_coverage": { + "complete": true, + "source_manifests": ["optimization-report.design-fixture.manifest.json"], + "entries": [ + { + "path": "/tmp/factory/prototypes/rack_unit.usd", + "role": "prototype", + "mesh_count": 412, + "disposition": "optimized", + "operations": ["meshCleanup", "fitPrimitives", "deduplicateGeometry", "decimateMeshes", "optimizeMaterials", "computeExtents"] + }, + { + "path": "/tmp/factory/factory.optimized.usdc", + "role": "assembly_root", + "mesh_count": 1840, + "disposition": "optimized", + "operations": ["meshCleanup", "fitPrimitives", "deduplicateGeometry", "decimateMeshes", "optimizeMaterials", "computeExtents"], + "notes": "Assembly-root remainder (meshes left after extraction) processed through the per-target op chain, not left to stage-level cleanup." + } + ] + } } diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/report-templates/optimization-report.design-fixture.manifest.json b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/report-templates/optimization-report.design-fixture.manifest.json new file mode 100644 index 00000000..9bf0604f --- /dev/null +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/report-templates/optimization-report.design-fixture.manifest.json @@ -0,0 +1,22 @@ +{ + "mode": "restructure", + "input_stage": "/tmp/factory.usd", + "output_dir": "/tmp/factory", + "new_assembly_root": "/tmp/factory/factory.optimized.usdc", + "phase4_targets": [ + { + "path": "/tmp/factory/prototypes/rack_unit.usd", + "target_class": "prototype", + "mesh_count": 412, + "dependency_group": "shared_first", + "source": "/World/Racks/Rack_01" + }, + { + "path": "/tmp/factory/factory.optimized.usdc", + "target_class": "assembly_root", + "mesh_count": 1840, + "dependency_group": "dependent_after", + "source": "/World" + } + ] +} diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/report-templates/optimization-report.md.template b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/report-templates/optimization-report.md.template index 4fbecadf..133155d9 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/report-templates/optimization-report.md.template +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/report-templates/optimization-report.md.template @@ -14,6 +14,18 @@ {{ reasoning }} +{% if runtime_context %} +## Runtime Context + +| Component | Value | +|---|---| +| Kit application | {{ runtime_context.kit.application }} {{ runtime_context.kit.version }} | +| Kit path | {{ runtime_context.kit.path }} | +| Kit build | {{ runtime_context.kit.build }} | +| Scene Optimizer | {{ runtime_context.sceneOptimizer.extension }} {{ runtime_context.sceneOptimizer.version }} | +| Asset Validator | {{ runtime_context.assetValidator.package }} {{ runtime_context.assetValidator.version }} (via {{ runtime_context.assetValidator.source }}) | +{% endif %} + ## Stage Impact Areas | Area | Score | Status | Notes | diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/report-templates/optimization-report.structural-only-fixture.json b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/report-templates/optimization-report.structural-only-fixture.json new file mode 100644 index 00000000..ba39d5f0 --- /dev/null +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/report-templates/optimization-report.structural-only-fixture.json @@ -0,0 +1,75 @@ +{ + "asset_name": "factory_main", + "input_path": "/data/factory_main.usd", + "output_path": null, + "timestamp": "2026-05-28T00:00:00Z", + "verdict": "neutral", + "workflow_mode": "structural_only", + "notes": "Scene Optimizer was unavailable in the selected runtime and the user declined install, so the workflow ran in structural-only mode: structure assessment plus pre-mutation USD validation only. No mesh operations executed and no optimized stage was written, so the verdict is neutral. FPS, frame time, and VRAM are unmeasured; re-run Phase 1a/6a in full mode under Kit / USD Composer / Isaac Sim to graduate the verdict.", + "optimization_score": 5.0, + "score_scope": "stage_optimization", + "score_label": "neutral", + "reasoning": "Scene Optimizer could not be loaded, so no mutation was attempted. Structure assessment characterized the stage and pre-mutation USD validation ran, but without SO there is no optimized output to compare against the baseline. The score reflects a neutral, no-change result rather than a measured optimization win.\n\nThe report documents the runtime blocker and the next capture needed so a later full-mode run can produce a real before/after verdict.", + "measurement_context": { + "profile_mode": "quick USD composition profile", + "runtime": "standalone USD Python (no Scene Optimizer)", + "score_scope": "stage/composition metrics only" + }, + "runtime_profiling": { + "status": "not_run", + "recommended_tool": "Omniperf", + "dashboard_url": null, + "artifact_path": null, + "summary": "Runtime profiling was not run for this report.", + "caveat": "Use Omniperf for RAM, VRAM, FPS, frame time, shader, renderer, and GPU metrics." + }, + "artifacts": { + "json": "/out/factory_main_optimization_report.json", + "markdown": "/out/factory_main_optimization_report.md", + "html": "/out/factory_main_optimization_report.html" + }, + "metric_groups": [ + { + "id": "composition", + "display_name": "Composition Complexity", + "score": 5.0, + "status": "measured", + "weight": 50, + "summary": "Composition characterized; no mutation applied in structural-only mode." + }, + { + "id": "validation", + "display_name": "Validation", + "score": 5.0, + "status": "measured", + "weight": 50, + "summary": "Pre-mutation USD validation ran; SO performance rules skipped (SO unavailable)." + } + ], + "metrics": [ + { + "name": "prim_count", + "display_name": "Prim Count", + "category": "composition", + "unit": "prims", + "direction": "lower_is_better", + "evidence_type": "direct", + "before": 184213, + "after": 184213, + "change_pct": 0.0, + "verdict": "neutral" + } + ], + "operations": [], + "validators": [ + { + "name": "MinimumOpenability", + "issues": 0, + "notes": "Stage opens; default prim, up-axis, and metersPerUnit present." + } + ], + "target_coverage": { + "complete": true, + "entries": [] + } +} diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/README.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/README.md index 403405a8..57526138 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/README.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/README.md @@ -78,9 +78,10 @@ validation workflows. The SO package includes `omni.scene.optimizer.validators` with `@register_rule` decorators that auto-register 25 SO performance validators into OAV when both packages share the same Python 3.12 environment. No manual `register_all()` call is needed -for rule discovery — just ensure both are importable. Category-scoped runs may -still use `ValidationEngine(init_rules=False)` plus `enable_rule()` for the -selected registered rule classes. +for rule discovery — just ensure both are importable. Selected runs go through +`usd-validation-runner/scripts/usd_validation_executor.py`, which uses +`ValidationEngine(init_rules=False)` plus `enable_rule()` after resolving each +scope-note **canonical concept** to a rule class by identity. > Standalone achieves the same validator coverage as Kit: install > `omniverse-asset-validator` via pip into the same venv where the SO package @@ -139,7 +140,7 @@ they are the preferred runtime (lighter, no Kit overhead, deterministic). Follow `references/standalone-runtime.md` for discovery and verification. If standalone packages are found and importable, set -`runtime_path: "standalone"` in `/setup-preflight.json` and +`runtime_route: "standalone"` in `/setup-preflight.json` and continue to Step 1.6. If standalone packages are not found, fall through to Step 1.5 (Kit discovery). @@ -150,24 +151,25 @@ If standalone is unavailable, look for Kit installations. Follow `references/kit-discovery.md` for discovery order, path classification, auto-enumeration, and candidate records. -Always ask before broad filesystem scanning. If one Kit candidate exists, set -`kit.chosen` to it and continue. If multiple candidates exist, ask the user to -choose; never silently pick one in an interactive session. The newest candidate -is pre-selected. +Always ask before broad filesystem scanning. If one Kit candidate exists, write +it to `runtime_context.kit` and continue. If multiple candidates exist, ask the +user to choose; never silently pick one in an interactive session. The newest +candidate is pre-selected. -Record the chosen candidate and `kit.chosen_by` as described in +Record the chosen candidate and `runtime_context.kit.chosen_by` as described in `references/kit-discovery.md`. ## Step 1.6 - Probe the chosen Kit for SO and AV versions -Once `kit.chosen` is set (or standalone is chosen), run the Python probe from -the chosen launcher and write the probe result to +Once `runtime_context.kit` is set (or standalone is chosen), run the Python +probe from the chosen launcher and write the probe result to `/setup-preflight.json`. Follow `references/runtime-probe.md` for the launcher, import-mode, version-source, and `operationsAvailable` contract. The `runtime_context` object is the literal input to the header template in `references/runtime-context-header.md`. Downstream skills read from this object, -not from the source `kit` / `sceneOptimizer` / `assetValidator` fields. +not from the raw probe `kit` / `sceneOptimizer` / `assetValidator` source +fields. Downstream skills (`so-run-operations`, `omniverse-usd-performance-tuning`, every `so-interpret-validators` recommendation) cross-check `operationsAvailable` @@ -188,6 +190,27 @@ When status is `needs-runtime-choice`, ask exactly for one of these paths: Do not continue to `so-run-validators`, `so-run-operations`, or deep validation until this choice is resolved. +## Non-interactive (batch / CI) mode + +The "stop and ask" behaviors above — the `output_path` prompt, the multiple-Kit +chooser, and the `needs-runtime-choice` gate — assume an interactive session. +For unattended batch or CI runs the caller can pre-supply those inputs, and the +agent must then proceed without blocking: + +- If `output_path`, a runtime preference, and any required candidate paths are + all supplied, do not prompt. +- When the preference is `auto`, resolve the runtime by deterministic policy: + 1. Standalone Scene Optimizer + Asset Validator, if importable. + 2. A user-supplied Kit / USD Composer / Isaac Sim path. + 3. The newest auto-discovered Kit — only when a broad filesystem scan was + explicitly authorized for this run. +- Record `runtime_context.kit.chosen_by: auto_policy` (or + `standalone_preferred`) in `setup-preflight.json` so downstream skills and the + report can show the runtime was selected unattended rather than confirmed by a + human. +- If no runtime resolves under this policy, stop with `needs-runtime-choice` and + name the missing inputs — do not guess a runtime or scan without permission. + ## Step 3 - Verify standalone path If standalone is chosen (Step 1 succeeded), verify each standalone requirement @@ -235,8 +258,8 @@ The header has two formats: - **Format B (compact one-liner)** — used for routine status messages and follow-up prompts once the user has already seen Format A in the session. -When `kit.chosen` is set (single candidate or user has picked), print Format A -once as the conclusion of this reference's interaction with the user, before the +When `runtime_context.kit` is set (single candidate or user has picked), print +Format A once as the conclusion of this reference's interaction with the user, before the agent hands off to `omniverse-usd-performance-tuning`. The user must see exactly which Kit application, Scene Optimizer, and Asset Validator version will be in effect for the rest of the session. diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/install-asset-validator-standalone/README.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/install-asset-validator-standalone/README.md index 93429e50..45e3d8a4 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/install-asset-validator-standalone/README.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/install-asset-validator-standalone/README.md @@ -89,9 +89,10 @@ print(f'Total rules: {len(list(registry.rules))}') Expected: `Usd:Performance` and `Omni:Geometry` categories appear with ~25 additional rules. No `register_all()` call is needed for rule discovery: the -`@register_rule` decorator on each checker class handles registration at import -time. Category-scoped runs may still use `ValidationEngine(init_rules=False)` -plus `enable_rule()` for selected registered rule classes. +validator registration decorators handle registration at import time. Category +names confirm discovery only; `usd-validation-runner` selects validators by +canonical concept and resolves them to rule classes by identity (via +`scripts/usd_validation_executor.py`) before calling `enable_rule()`. ## Output diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/install-so-standalone/README.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/install-so-standalone/README.md index 9c3e5984..1f22efa0 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/install-so-standalone/README.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/install-so-standalone/README.md @@ -33,6 +33,10 @@ project-managed `omniverse-asset-validator` environment that can import the same SO package. Kit remains useful when automatic extension registration or render-time profiling is needed. +This install reference does not define operation invocation. Keep operation +execution examples in `so-run-operations/references/invocation.md` so agents +have one source of truth. + ## Prerequisites > **Python 3.12 is a HARD requirement.** The drop ships `cp312`-only wheels. @@ -144,31 +148,29 @@ process, so the uv-managed-Python caveat above does not apply. python3.12 - <<'PY' def operation_count(): try: - from omni.scene.optimizer.core.bindings._omni_scene_optimizer_core import acquire_interface + from omni.scene.optimizer.core import SceneOptimizerCore - iface = acquire_interface() - if hasattr(iface, "get_operations"): - return "bindings.acquire_interface", len(iface.get_operations()) + return "SceneOptimizerCore.getInstance", len(SceneOptimizerCore.getInstance().getOperations()) except Exception: pass - import omni.scene.optimizer.core as soc + from omni.scene.optimizer.core.bindings._omni_scene_optimizer_core import acquire_interface - core = soc.SceneOptimizerCore.getInstance() - return "SceneOptimizerCore.getInstance", len(core.getOperations()) + iface = acquire_interface() + if hasattr(iface, "get_operations"): + return "bindings.acquire_interface", len(iface.get_operations()) + parser = iface.json_parser() + return "bindings.json_parser", len(parser.get_supported_operations()) surface, count = operation_count() print(f"{surface}: {count} operations") PY ``` -Expect >= 40 (the exact count varies by build). Standalone drops do not all -expose the same Python API: SO 110.0.2-style environments may require -`acquire_interface().get_operations()` / -`acquire_interface().execute_operation(op, context, json_string)`, while other -builds expose `SceneOptimizerCore.getInstance().getOperations()` / -`executeOperation`. Operation scripts must probe the selected runtime before -choosing an API surface. +Expect >= 40 (the exact count varies by build). This verifies import and +operation registry only. Operation invocation is defined by +`so-run-operations/references/invocation.md`; do not infer mutation call shapes +from this install probe. ## Limitations @@ -195,32 +197,30 @@ registry = CategoryRuleRegistry() # Now includes "Usd:Performance" and "Omni:Geometry" categories ``` -No `register_all()` call is needed for rule discovery. The -`@register_rule("Usd:Performance")` decorator on each checker class handles -registration at import time. +No `register_all()` call is needed for rule discovery. The rule registration +decorators handle registration at import time. Do not treat category names as +validation scope, and do not select rules by bare name — the canonical executor +resolves a scope note's concepts to rule classes by identity (a bare +`find_rule()` can't tell the Scene Optimizer and Asset Validator rules that +share a class name apart). -To run only SO performance rules (excluding base OAV rules): +To verify the install can run a scoped concept after `usd-validation-runner` +has scoped the plan: ```python -import omni.scene.optimizer.validators # auto-registers -from omni.asset_validator import ValidationEngine, CategoryRuleRegistry -from pxr import Usd - -registry = CategoryRuleRegistry() -so_rules = [ - rule - for category in ("Usd:Performance", "Omni:Geometry") - for rule in registry.get_rules(category) -] - -engine = ValidationEngine(init_rules=False) -for rule in so_rules: - engine.enable_rule(rule) - -stage = Usd.Stage.Open("path/to/asset.usd") -results = engine.validate(stage) +from usd_validation_executor import load_registry, validate_concepts + +registry = load_registry() +issues = validate_concepts( + "path/to/asset.usd", + ["primvar_indexability"], # canonical concept from the scope note + registry=registry, +) ``` +The executor builds the engine with `init_rules=False` and enables only the +resolved rule class. + The standalone import is `from omni.asset_validator import ValidationEngine` (no `.core`). The `.core` submodule only exists inside a running Kit session. diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/install-so-via-kit/README.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/install-so-via-kit/README.md index 28609cf0..464e6d07 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/install-so-via-kit/README.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/install-so-via-kit/README.md @@ -51,8 +51,8 @@ from omni.kit_app import KitApp import sys app = KitApp() app.startup(['--no-window', '--enable', 'omni.scene.optimizer.core']) -import omni.scene.optimizer.core as soc -print(len(soc.acquire_interface().get_operations())) +from omni.scene.optimizer.core import SceneOptimizerCore +print(len(SceneOptimizerCore.getInstance().getOperations())) sys.exit(app.shutdown()) " ``` @@ -61,10 +61,9 @@ Expect ≥ 40 (floor — varies by version). First run pulls SO from the registry (~minutes); subsequent runs are cached under `~/.local/share/ov/data/Kit/`. -The in-Kit verification path uses `soc.acquire_interface().get_operations()`. -Different standalone drops can expose either this lower-level interface or the -`SceneOptimizerCore` wrapper. Operation scripts should probe the selected -runtime and use the API surface it actually provides. +The in-Kit verification path uses the public `SceneOptimizerCore` registry. +Operation invocation is defined by `so-run-operations/references/invocation.md`; +do not infer mutation call shapes from this install probe. ## Remote Omniverse assets diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/kit-discovery.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/kit-discovery.md index aaeb2aae..54a70c77 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/kit-discovery.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/kit-discovery.md @@ -82,11 +82,17 @@ does not encode them. Sort candidates by semantic version descending. ## User Selection -If one candidate exists, set `kit.chosen` to it and continue. +The enumerated `kit.candidates[]` are the raw discovery source. The selected +candidate becomes the canonical runtime: copy its `application`, `version`, +`path`, and `build` into the `runtime_context.kit` object (the block the header +prints and downstream skills consume). Do not keep a separate `kit.chosen` +copy — `runtime_context.kit` is the single source of truth. + +If one candidate exists, write it to `runtime_context.kit` and continue. If multiple candidates exist, always ask. Pre-select the newest candidate, add `Use standalone libraries instead` as the final option, and record: -- `kit.chosen_by: "user"` for interactive selection. -- `kit.chosen_by: "unattended_default"` when no user input channel exists and - the newest candidate is automatically selected. +- `runtime_context.kit.chosen_by: "user"` for interactive selection. +- `runtime_context.kit.chosen_by: "unattended_default"` when no user input + channel exists and the newest candidate is automatically selected. diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/runtime-context-header.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/runtime-context-header.md index 108f7564..58392d69 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/runtime-context-header.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/runtime-context-header.md @@ -89,11 +89,11 @@ The gate's steps: ``` ─── Runtime context ─────────────────────────────────────────────────────── - Kit application: {kit.chosen.application} {kit.chosen.version} - path: {kit.chosen.path} - build: {kit.chosen.build} - Scene Optimizer: {sceneOptimizer.extension} {sceneOptimizer.version} - Asset Validator: {assetValidator.package} {assetValidator.version} via {assetValidator.source} + Kit application: {runtime_context.kit.application} {runtime_context.kit.version} + path: {runtime_context.kit.path} + build: {runtime_context.kit.build} + Scene Optimizer: {runtime_context.sceneOptimizer.extension} {runtime_context.sceneOptimizer.version} + Asset Validator: {runtime_context.assetValidator.package} {runtime_context.assetValidator.version} via {runtime_context.assetValidator.source} ─────────────────────────────────────────────────────────────────────────── This runtime will be used for the work that follows. Continue, or change it? @@ -128,17 +128,17 @@ run the gate instead. ## Source of truth -Both formats below read from `/setup-preflight.json` (canonical filename + location; see *Where artifacts live* above). The fields the header consumes are: +Both formats below read from the **`runtime_context`** object in `/setup-preflight.json` (canonical filename + location; see *Where artifacts live* above). `runtime_context` is the canonical block the probe writes and downstream skills consume; the header never reads the raw probe `kit` / `sceneOptimizer` / `assetValidator` source fields directly. The fields the header consumes are: -- `kit.chosen.application` — friendly name (e.g. `USD Composer`, `Isaac Sim`, `Kit SDK`) -- `kit.chosen.version` — release version (e.g. `110.1.0`) -- `kit.chosen.path` — absolute install path -- `kit.chosen.build` — full build identifier when present (e.g. `110.1.0+main.10181.f4b28ef2.gl.windows-x86_64.release`) -- `sceneOptimizer.extension` — extension name (e.g. `omni.scene.optimizer.core`) -- `sceneOptimizer.version` — extension version -- `assetValidator.package` — package or extension name -- `assetValidator.version` — version -- `assetValidator.source` — `kit-extension` or `pip` (informs the user whether AV runs through Kit or as a standalone Python install) +- `runtime_context.kit.application` — friendly name (e.g. `USD Composer`, `Isaac Sim`, `Kit SDK`) +- `runtime_context.kit.version` — release version (e.g. `110.1.0`) +- `runtime_context.kit.path` — absolute install path +- `runtime_context.kit.build` — full build identifier when present (e.g. `110.1.0+main.10181.f4b28ef2.gl.windows-x86_64.release`) +- `runtime_context.sceneOptimizer.extension` — extension name (e.g. `omni.scene.optimizer.core`) +- `runtime_context.sceneOptimizer.version` — extension version +- `runtime_context.assetValidator.package` — package or extension name +- `runtime_context.assetValidator.version` — version +- `runtime_context.assetValidator.source` — `kit-extension`, `pip`, or `standalone` (informs the user whether AV runs through Kit or as a standalone Python install) If `/setup-preflight.json` is unavailable when an agent reaches a prompt that requires the header, it must invoke `setup-usd-performance-tuning` first. The header must never be skipped or partially filled. @@ -153,11 +153,11 @@ Use at every decision point where the user is authorizing something that mutates ``` ─── Runtime context ─────────────────────────────────────────────────────── -Kit application: {kit.chosen.application} {kit.chosen.version} - path: {kit.chosen.path} - build: {kit.chosen.build} -Scene Optimizer: {sceneOptimizer.extension} {sceneOptimizer.version} -Asset Validator: {assetValidator.package} {assetValidator.version} via {assetValidator.source} +Kit application: {runtime_context.kit.application} {runtime_context.kit.version} + path: {runtime_context.kit.path} + build: {runtime_context.kit.build} +Scene Optimizer: {runtime_context.sceneOptimizer.extension} {runtime_context.sceneOptimizer.version} +Asset Validator: {runtime_context.assetValidator.package} {runtime_context.assetValidator.version} via {runtime_context.assetValidator.source} ─────────────────────────────────────────────────────────────────────────── ``` @@ -167,8 +167,10 @@ If the user has more than one Kit installed and the workflow has not yet committ Use for routine status messages, ack messages, and follow-up prompts in the same session where the user has already seen Format A. +This file is the **single source of truth** for the Format B string. Any skill that prints it (`omniverse-usd-performance-tuning` initial ack, `compare-profiles` verdict header) must reproduce it character-for-character: + ``` -[Kit: {kit.chosen.application} {kit.chosen.version} | SO: {sceneOptimizer.version} | AV: {assetValidator.version}] +[Kit: {runtime_context.kit.application} {runtime_context.kit.version} | SO: {runtime_context.sceneOptimizer.version} | AV: {runtime_context.assetValidator.version}] ``` Required at: diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/runtime-probe.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/runtime-probe.md index 563ba71e..f064f87e 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/runtime-probe.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/runtime-probe.md @@ -88,12 +88,12 @@ Prefer these sources in order: For supported SO operation keys, use this fallback chain: ```python -# Preferred (standalone prebuilt with impl.core): -from omni.scene.optimizer.impl.core import SceneOptimizerCore +# Preferred: +from omni.scene.optimizer.core import SceneOptimizerCore inst = SceneOptimizerCore.getInstance() ops = inst.getOperations() # returns iterable of operation names -# Fallback (Kit and some standalone builds with bindings): +# Fallback for lower-level binding-only builds: omni.scene.optimizer.core.bindings._omni_scene_optimizer_core \ .acquire_interface().json_parser().get_supported_operations() ``` diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/scripts/probe-snapshot.schema.json b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/scripts/probe-snapshot.schema.json index a032babf..67a5e314 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/scripts/probe-snapshot.schema.json +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/scripts/probe-snapshot.schema.json @@ -23,7 +23,7 @@ "type": "array", "items": { "type": "string" }, "uniqueItems": true, - "description": "Operation keys the SO extension registers, as returned by acquire_interface().get_operations(). Sorted alphabetically." + "description": "Operation keys the SO runtime registers, as returned by the selected operation registry probe. Sorted alphabetically." }, "probed_at": { "type": "string", diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/scripts/setup-preflight.schema.json b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/scripts/setup-preflight.schema.json index 0b4680c5..2770872c 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/scripts/setup-preflight.schema.json +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/scripts/setup-preflight.schema.json @@ -76,8 +76,65 @@ }, "runtime_context": { "type": "object", - "description": "Runtime context block that downstream skills (optimization-report) consume. Structure mirrors the optimization-report schema's runtime_context definition.", - "additionalProperties": true + "description": "Canonical runtime context block that downstream skills (optimization-report, the runtime-context-header) consume. The probe writes the chosen runtime here and the header prints from it; the source kit/sceneOptimizer/assetValidator fields above are the raw probe data. Inner shape matches the optimization-report schema's runtime_context definition exactly so the block can be copied verbatim into the report.", + "required": ["kit", "sceneOptimizer", "assetValidator"], + "properties": { + "kit": { + "type": "object", + "required": ["application", "version", "path"], + "properties": { + "application": { + "type": "string", + "description": "Friendly name of the Kit application, e.g. 'USD Composer', 'Isaac Sim', 'Kit SDK'." + }, + "version": { + "type": "string", + "description": "Release version, e.g. '110.1.0'." + }, + "path": { + "type": "string", + "description": "Absolute path to the Kit root." + }, + "build": { + "type": ["string", "null"], + "description": "Full build identifier when present (e.g. '110.1.0+main.10181....release'); null when the install path does not encode one." + } + } + }, + "sceneOptimizer": { + "type": "object", + "required": ["extension", "version"], + "properties": { + "extension": { + "type": "string", + "description": "Extension name, typically 'omni.scene.optimizer.core'." + }, + "version": { + "type": "string", + "description": "Extension version, e.g. '110.0.4'." + } + } + }, + "assetValidator": { + "type": "object", + "required": ["package", "version", "source"], + "properties": { + "package": { + "type": "string", + "description": "Package or extension name, e.g. 'omniverse-asset-validator' or 'omni.asset_validator.core'." + }, + "version": { + "type": "string", + "description": "Package version." + }, + "source": { + "type": "string", + "enum": ["kit-extension", "pip", "standalone"], + "description": "Where Asset Validator was loaded from." + } + } + } + } }, "probed_at": { "type": "string", diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/skill-map.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/skill-map.md index 936dbe11..18c8e1d0 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/skill-map.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/skill-map.md @@ -104,7 +104,7 @@ flowchart TD P4["Phase 4 Per-asset SO ops"] P5["Phase 5 Ref remap + cleanup"] P6["Phase 6 Verify + report"] - P7["Phase 7 Optional iteration"] + P7["Phase 7 Default scoped iteration"] P0 --> P1 --> P2 P2 -->|"already optimized or exit"| P6 P2 -->|"continue"| P3 --> P4 --> P5 --> P6 @@ -116,7 +116,7 @@ flowchart TD - Optimization workflow: `skills/omniverse-usd-performance-tuning/references/workflow.md` - Runtime artifact/token policy: `skills/omniverse-usd-performance-tuning/references/runtime-artifact-token-budget.md` -- Validation routing: `skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/validation-scoping.md` +- Validation routing: `skills/omniverse-usd-performance-tuning/references/usd-validation-runner/README.md` - Validation command references: `skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/` - Scene Optimizer operation mechanics: [`usd-optimize`](https://github.com/NVIDIA-omniverse/usd-optimize/) or the diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/so-run-operations/README.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/so-run-operations/README.md index a39c5806..07a94aac 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/so-run-operations/README.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/so-run-operations/README.md @@ -30,6 +30,8 @@ reads. Do not clone the source repo just to read upstream SO guidance. - Apply local output workspace policy and `runtime-artifact-token-budget.md`; keep logs on disk and read bounded summaries only. - Apply destructive-operation approval gates via `references/operation-safety.md` before mutation. - Keep digitaltwin evidence-to-config routing in `references/config-from-evidence.md`. +- Treat `references/invocation.md` as the only local source of truth for + Python/API invocation shapes. - For Phase 4b multi-target optimization, use `references/batch-mode.md` for target enumeration, adaptive concurrency, prototype-first ordering, hash-based output names, resource observations, and remainder-script prompts. - Preserve logical milestone name `so-run-operations` and hand results to profile/compare/report phases. @@ -45,13 +47,14 @@ Before executing the op chain, re-read and confirm: - [ ] Per-op `parameter_prerequisites` frontmatter read for each destructive op. - [ ] `references/units-and-tolerances.md` — conversion formula for any tolerance-based op. -- [ ] Upstream `usd-optimize` run-operations guide for invocation mechanics. +- [ ] `references/invocation.md` for local invocation mechanics and upstream + handoff. - [ ] `references/batch-mode.md` for multi-target orchestration. - [ ] `runtime-artifact-token-budget.md` §"Stderr Production Guard" — redirect subprocess stderr, cap at 50 MB, retain head/tail samples. ## Execution Handoff -Use upstream `run-operations` for invocation mechanics, operation source forms, +Use `references/invocation.md` for supported Python/API invocation shapes, optional helper wrappers, selected-runtime API probing, output saving, and generic failure handling. Use this local file only for digitaltwin workflow gating, batch orchestration policy, and reporting policy. diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/so-run-operations/references/batch-mode.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/so-run-operations/references/batch-mode.md index 94857af0..add3edf0 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/so-run-operations/references/batch-mode.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/so-run-operations/references/batch-mode.md @@ -17,7 +17,9 @@ Targets come from: - `apply-restructure` mode=`restructure`: prototype USDs, shared layers, and newly loadable sub-assets recorded in - `/apply-restructure-manifest.json` `phase4_targets[]`. + `/apply-restructure-manifest.json` `phase4_targets[]`, plus any + `target_class: "assembly_root"` entry for mesh data retained in the assembly. + Do not filter the manifest to prototype files only. - Composed stages with no restructure: referenced sub-assets from `usd-structure-assessment` Phase 1.2 `assets.manifest`. - Monolithic-as-is: the original stage (`N=1`). @@ -63,7 +65,10 @@ risk says continuing automatically is unsafe. When targets include prototypes and non-prototype assets, run prototypes first, wait for completion, then run non-prototype assets. Parallelize within each dependency group according to the adaptive concurrency policy. Prototype changes -propagate to instances, so running instance-site work first wastes time. +propagate to instances, so running instance-site work first wastes time. Treat +an `assembly_root` target with retained meshes as a non-prototype mesh target: +run the evidence-selected per-target mesh op chain on it before final +assembled-root cleanup. ## Output Naming diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/so-run-operations/references/invocation.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/so-run-operations/references/invocation.md index d547fd8d..771401e3 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/so-run-operations/references/invocation.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/so-run-operations/references/invocation.md @@ -7,15 +7,16 @@ How to execute Scene Optimizer operations once the runtime is selected and the operation plan is approved. Read `/setup-preflight.json` to determine which runtime and API surface to use. +This is the local source of truth for Scene Optimizer operation invocation. +Other workflow docs should link here instead of repeating Python API snippets. + The two runtimes below are peers — neither is preferred. The user's Phase 0 choice determines which section applies. ## Kit Runtime When `setup-preflight.json` indicates Kit as the selected runtime, bootstrap -Kit first, then use the same Python API as standalone. Kit and standalone -environments both expose `SceneOptimizerCore.getInstance()` with -`executeOperation` or `executeConfig`. +Kit first, then use the same supported Python shapes as standalone. ```python import os @@ -35,29 +36,38 @@ app.startup([ # "--enable", "omni.usd_resolver", ]) -import omni.scene.optimizer.core as soc +from omni.scene.optimizer.core import ExecutionContext, SceneOptimizerCore from pxr import Usd # Open stage stage = Usd.Stage.Open(input_path) -# Get optimizer instance -core = soc.SceneOptimizerCore.getInstance() +# Attach the stage to an ExecutionContext before direct API calls. +context = ExecutionContext() +context.set_stage(stage) +core = SceneOptimizerCore.getInstance() # Verify operations are available ops = core.getOperations() # Execute a single operation -config_json = '{"operation": "meshCleanup", "mergeVertices": true}' -core.executeOperation("meshCleanup", stage, config_json) +success, error, output = core.executeOperation( + "meshCleanup", + context, + {"mergeVertices": True}, +) +if not success: + raise RuntimeError(error) # Or execute a pipeline -pipeline_json = '''[ - {"operation": "meshCleanup", "mergeVertices": true}, +pipeline = [ + {"operation": "meshCleanup", "mergeVertices": True}, {"operation": "optimizeMaterials"}, - {"operation": "pruneLeaves"} -]''' -core.executeConfig(stage, pipeline_json) + {"operation": "pruneLeaves"}, +] +for success, error, output in core.executeConfig(context, pipeline): + if not success: + raise RuntimeError(error) # Export optimized output (never overwrite source) stage.Export(output_path) @@ -70,12 +80,9 @@ sys.exit(app.shutdown()) - Cross-check every operation key against `operationsAvailable` in `setup-preflight.json` before execution. If missing, report `blocked_missing_so_operation`. -- Probe the selected runtime before writing the script. Some builds - expose the C++ binding interface from - `omni.scene.optimizer.core.bindings._omni_scene_optimizer_core` instead - of `SceneOptimizerCore`. Use whichever the runtime provides. +- Probe the selected runtime before writing the script. - Set `OMNI_KIT_ACCEPT_EULA=yes` in the environment before KitApp import. -- For analysis-only operations, set `"analysisMode": 1` in the config JSON. +- For analysis-only operations, set `context.analysisMode = 1`. - Operation keys come from the per-operation page's Parameters table and starting-config JSON. Invalid keys may warn or silently no-op. - First run may spend minutes fetching extensions from the registry; subsequent @@ -103,6 +110,62 @@ by the user. - Write optimized stages and runtime artifacts under the local output workspace chosen by setup. +## Verified Python API Shapes + +Verified against +`scene_optimizer_core_usd_25.11_py_3.12@110.1.0+master.401.324ccecb.gl.manylinux_2_35_x86_64.release`. + +Preferred public JSON API: + +```python +import json +from omni.scene.optimizer.core.scripts import standalone +from pxr import Usd + +stage = Usd.Stage.Open(input_path) +ok = standalone.execute_commands_from_json(stage, json.dumps([ + {"operation": "meshCleanup", "mergeVertices": True}, +])) +if not ok: + raise RuntimeError("Scene Optimizer operation chain failed") +stage.Export(output_path) +``` + +Direct API with per-operation results: + +```python +from omni.scene.optimizer.core import ExecutionContext, SceneOptimizerCore +from pxr import Usd + +stage = Usd.Stage.Open(input_path) +context = ExecutionContext() +context.set_stage(stage) +results = SceneOptimizerCore.getInstance().executeConfig(context, [ + {"operation": "meshCleanup", "mergeVertices": True}, +]) +for success, error, output in results: + if not success: + raise RuntimeError(error) +stage.Export(output_path) +``` + +## Invalid Call Shape + +Do not pass a plain `pxr.Usd.Stage` directly as the second argument to +`SceneOptimizerCore.executeOperation` or `executeConfig`. The binding expects an +`ExecutionContext`; the stage must be attached with `context.set_stage(stage)`. +The bad shape below reproduces the failure seen in Horde testing: + +```python +SceneOptimizerCore.getInstance().executeOperation("printStats", stage, {}) +# AttributeError: 'Stage' object has no attribute '_impl' +``` + +If `_impl` appears in an operation log, stop the operation pass, mark the +attempt as an invalid SO invocation, and rerun through the supported shapes +above. Do not export or report a successful optimized stage from that failed +pass. + ## Save Policy - Export optimized output to a NEW `.usdc` path under `/`. diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/so-run-operations/references/so-create-proxy/references/proxy-config-recipes.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/so-run-operations/references/so-create-proxy/references/proxy-config-recipes.md index 198d4a47..4cfd4828 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/so-run-operations/references/so-create-proxy/references/proxy-config-recipes.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/so-run-operations/references/so-create-proxy/references/proxy-config-recipes.md @@ -6,23 +6,19 @@ This local reference preserves the digitaltwin workflow milestone. Scene Optimizer mechanics for this step are owned by upstream `usd-optimize`. -- Public repository: [https://github.com/NVIDIA-omniverse/usd-optimize/](https://github.com/NVIDIA-omniverse/usd-optimize/) -- Package path: `.agents/skills/create-proxy/references/decimate-mode.md` -- Upstream web URL: [https://github.com/NVIDIA-omniverse/usd-optimize/blob/main/.agents/skills/create-proxy/references/decimate-mode.md](https://github.com/NVIDIA-omniverse/usd-optimize/blob/main/.agents/skills/create-proxy/references/decimate-mode.md) +Proxy config recipes are composed from the per-mode sibling handoffs in this +folder rather than restating the same upstream doc. To avoid a duplicate +upstream-doc reference, this stub points to those siblings instead of +re-declaring the package path: -Resolve the upstream guide without cloning the source repo: +- Decimate-based proxy configs: see [`decimate-step-recipes.md`](decimate-step-recipes.md) + (upstream `create-proxy/references/decimate-mode.md`). +- Decimation parameter tuning: see [`decimation-tuning.md`](decimation-tuning.md) + (upstream `create-proxy/references/parameter-tuning.md`). +- Bounding-box proxy configs: see [`bounding-box-proxy-modes.md`](bounding-box-proxy-modes.md) + (upstream `create-proxy/references/bounding-box-modes.md`). -1. `$SCENE_OPTIMIZER_PACKAGE_ROOT/.agents/skills/create-proxy/references/decimate-mode.md` -2. `$SO_HOME/.agents/skills/create-proxy/references/decimate-mode.md` - -If no package root is available, download and extract the published -`scene_optimizer_core_...release.zip` package for the target platform (direct -archive URLs are in `references/upstreams/usd-optimize.md`), or use the package -path/URL supplied by the user. If the user supplies an extracted -package root directly, resolve this same package path under that root. If -GitHub raw fetch is available, the web URL above is acceptable for docs-only -reads. Do not clone the source repo just to read upstream SO guidance. - -For bounding-box configs, use upstream -`.agents/skills/create-proxy/references/bounding-box-modes.md` from the same -package root or public repo. +For the public repository and package-root resolution rules, follow the sibling +handoff above for the relevant mode. Direct archive URLs are in +`references/upstreams/usd-optimize.md`. Do not clone the source repo just to +read upstream SO guidance. diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/upstreams/README.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/upstreams/README.md new file mode 100644 index 00000000..6b1244ab --- /dev/null +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/upstreams/README.md @@ -0,0 +1,23 @@ +# Upstream Source-of-Truth References + + + + +Pointers to the upstream repositories and prebuilt packages this skill delegates +to instead of reimplementing. Operation mechanics, parameters, defaults, and +package resolution live upstream; this skill owns only the digital twin workflow +routing, runtime setup, validation scope, output policy, and reporting that wrap +them. + +When a file here names a tool, prefer the upstream URL it records for the most +current version — the local notes are a snapshot and a resolution recipe, not a +copy of the upstream docs. + +## Contents + +- [`usd-optimize.md`](usd-optimize.md) — Scene Optimizer operation mechanics and + prebuilt-package resolution (upstream + [usd-optimize](https://github.com/NVIDIA-omniverse/usd-optimize/)). Resolve + per-operation guides through `$SCENE_OPTIMIZER_PACKAGE_ROOT` / `$SO_HOME` or + the upstream `.agents/operations/.md` path rather than duplicating them + in this repo. diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/README.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/README.md index 7cd37560..cf794558 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/README.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/README.md @@ -54,11 +54,11 @@ Before producing the SA report, re-read and confirm: ## Limitations -- Phase 1 is purely structural: metadata, composition arcs, prim traversal. +- SA Stage 1 is purely structural: metadata, composition arcs, prim traversal. No geometry arrays (points, faceVertexCounts) are read. No renderer, viewport, or BBoxCache computation. Mesh-level stats (triangle counts, density) are deferred to Phase 2c validators (SO analysis mode). -- Phase 2 heuristics flag validation candidates; they do not justify operations +- SA Stage 2 heuristics flag validation candidates; they do not justify operations by themselves. - Outlier detection (§2.1) uses authored `extentsHint` attributes when present. If extents are not authored, SA cannot flag spatial outliers — Phase 2c @@ -80,7 +80,7 @@ Before running, read: If you have network access, prefer the live URLs (noted in each reference file). -## Phase 1: Structure Analysis (no geometry load) +## SA Stage 1: Structure Analysis (no geometry load) These checks run without a Kit viewport, GPU, or renderer. SA opens the stage with `Usd.Stage.Open(path)` (default load rules) and traverses the composed @@ -151,7 +151,7 @@ Report: - Flag prims with geometry but no kind assignment. - Flag kind assignments that don't match the hierarchy (e.g., a component inside a component). -## Phase 2: Structural Heuristics (metadata only, narrows validation scope) +## SA Stage 2: Structural Heuristics (metadata only, narrows validation scope) These checks use authored extent metadata (`extentsHint`) and structural patterns to identify assets that likely need deep validation. They do not @@ -458,9 +458,9 @@ The `validation_scope` section feeds directly into `so-run-validators` — it te the agent which assets to validate and which to skip. The `summary_counts` section is the compact handoff consumed by -`usd-validation-runner` and `skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/validation-scoping.md`: it tells the -validator router whether the asset is monolithic, prototype-heavy, already -instanced, or worth restructuring before expensive validators run. +`usd-validation-runner`: it tells the validator router whether the asset is +monolithic, prototype-heavy, already instanced, or worth restructuring before +expensive validators run. For `reference_count` and `payload_count`, prefer authored list-op item counts. If the selected USD Python runtime can only produce prim-level booleans, use diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/apply-restructure/README.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/apply-restructure/README.md index 6a9eb64e..855e03c6 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/apply-restructure/README.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/apply-restructure/README.md @@ -50,8 +50,8 @@ Before executing restructure writes, re-read and confirm: never overwrites the original stage in-place. - [ ] `setup-preflight.json` runtime context — confirm USD Python environment is available for authoring. -- [ ] After restructure: re-validate (Phase 3a sweep) to confirm no - composition breaks. +- [ ] After restructure: run scoped re-validation to confirm no composition + breaks. ## Limitations @@ -108,7 +108,8 @@ Manifest schema: "phase4_targets": [ { "path": "", - "target_class": "prototype | shared_layer | loadable_subasset", + "target_class": "prototype | shared_layer | loadable_subasset | assembly_root", + "mesh_count": 0, "dependency_group": "shared_first | dependent_after | independent", "source": "", "weight_hints": { @@ -133,6 +134,17 @@ Manifest schema: } ``` +The strict contract for this file is `scripts/apply-restructure-manifest.schema.json`. +Every `phase4_targets[]` entry MUST carry a top-level `mesh_count` (integer >= 0): +the authoritative default-predicate count +(`len([p for p in Usd.PrimRange.Stage(stage, Usd.PrimDefaultPredicate) if p.IsA(UsdGeom.Mesh)])`) +measured with the target opened standalone, matching the Postcondition below. +`weight_hints.mesh_count` remains an optional, non-authoritative batching estimate. +The downstream Phase-4 completion gate (`optimization-report/scripts/validate_report.py +--manifest`) reconciles the final report's `target_coverage` against the UNION of +every iteration's `phase4_targets[]` and accepts a `skipped_zero_meshes` disposition +only when this `mesh_count` is `0`, so a retained-mesh target cannot be silently dropped. + ## Preconditions - `input_stage` opens cleanly (use `validate-usd-minimum` to confirm before starting). @@ -161,7 +173,9 @@ Manifest schema: - For `mode=restructure`: the manifest documents what mesh content remains on the assembly root after extraction. If the assembly root has > 0 mesh prims, include it in `phase4_targets[]` with `target_class: "assembly_root"` so - Phase 4 does not skip it. + Phase 4 does not skip it. Downstream Phase 4 must process that entry through + the per-target mesh op chain for its retained meshes; it is not limited to + final stage-level cleanup operations. --- diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/apply-restructure/references/restructure-mode.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/apply-restructure/references/restructure-mode.md index ccfb4d70..d32d33cd 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/apply-restructure/references/restructure-mode.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/apply-restructure/references/restructure-mode.md @@ -3,8 +3,9 @@ # Restructure Mode -Use this reference for `apply-restructure` mode=`restructure` after -`restructure-decision` returns `proceed`. +Use this reference for `apply-restructure` mode=`restructure`, invoked when +`restructure-decision` selects the `extract-as-assets` or +`decompose-for-selective-loading` branch. ## Internal-Reference Scan @@ -79,7 +80,7 @@ Execution order: a. Create the interface + payload layers following the reference-payload pattern above. b. Set `instanceable=true` on the payload root prim only when - `instancing-readiness` (see `restructure-decision/README.md` + `instancing-readiness` (see `skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/restructure-decision/README.md` §"instancing-readiness gate") passes for that site's dedupe group. c. Reference the site's interface layer from the assembly root. 3. For unique (non-duplicate) boundary candidates, extract as independent diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/apply-restructure/scripts/apply-restructure-manifest.schema.json b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/apply-restructure/scripts/apply-restructure-manifest.schema.json new file mode 100644 index 00000000..6e8d269b --- /dev/null +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/apply-restructure/scripts/apply-restructure-manifest.schema.json @@ -0,0 +1,84 @@ +{ + "$comment": "SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\nSPDX-License-Identifier: Apache-2.0", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Apply-Restructure Manifest", + "description": "Contract for /apply-restructure-manifest.json, the Phase 2f -> Phase 4 handoff. phase4_targets[] is the authoritative list of files Phase 4 must mesh-optimize; the final optimization-report's target_coverage must cover the UNION of every iteration's phase4_targets (see validate_report.py --manifest). A mode=restructure manifest MUST carry a non-empty phase4_targets[], and every entry MUST declare its default-predicate mesh_count so a 'skipped_zero_meshes' disposition cannot be faked.", + "type": "object", + "required": ["mode", "phase4_targets"], + "properties": { + "mode": { + "type": "string", + "enum": ["restructure", "ref_remap"] + }, + "input_stage": { "type": "string" }, + "output_dir": { "type": "string" }, + "new_assembly_root": { "type": "string" }, + "outputs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { "type": "string" }, + "kind": { + "type": "string", + "enum": ["prototype", "shared_layer", "loadable_subasset", "parent_assembly", "new_root"] + }, + "provenance": { "type": "string" }, + "size_bytes": { "type": "integer", "minimum": 0 }, + "validate_usd_minimum": { "type": "string", "enum": ["pass", "fail", "skipped"] }, + "notes": { "type": "string" } + } + } + }, + "phase4_targets": { + "type": "array", + "description": "Every written prototype/shared layer/loadable sub-asset that needs Phase 4 mesh optimization, PLUS the assembly root itself when it retains > 0 meshes after extraction.", + "items": { + "type": "object", + "required": ["path", "target_class", "mesh_count"], + "properties": { + "path": { + "type": "string", + "description": "Written file Phase 4 must optimize. This is the reconciliation key against optimization-report.target_coverage[].path." + }, + "target_class": { + "type": "string", + "enum": ["prototype", "shared_layer", "loadable_subasset", "assembly_root"] + }, + "mesh_count": { + "type": "integer", + "minimum": 0, + "description": "Authoritative default-predicate mesh count: len of Usd.PrimRange.Stage(stage, Usd.PrimDefaultPredicate) filtered to UsdGeom.Mesh, measured when the target is opened standalone (matches the apply-restructure Postcondition). The Phase-4 completion gate accepts disposition 'skipped_zero_meshes' only when this is 0." + }, + "dependency_group": { + "type": "string", + "enum": ["shared_first", "dependent_after", "independent"] + }, + "source": { "type": "string" }, + "weight_hints": { + "type": "object", + "description": "Optional pre-extraction estimates for adaptive batching. NON-authoritative; mesh_count above is the authoritative count the gate uses." + }, + "notes": { "type": "string" } + } + } + }, + "rewrite_steps": { "type": "array" }, + "material_rewrites": { "type": "array" }, + "warnings": { "type": "array" } + }, + "allOf": [ + { + "if": { + "properties": { "mode": { "const": "restructure" } }, + "required": ["mode"] + }, + "then": { + "properties": { + "phase4_targets": { "minItems": 1 } + }, + "required": ["phase4_targets"] + } + } + ] +} diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/composition-audit.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/composition-audit.md index e0c6de2d..cb503722 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/composition-audit.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/composition-audit.md @@ -3,7 +3,7 @@ # USD Composition Audit -> Composition audit is performed as part of `usd-structure-assessment` Phase 1.1; this reference holds the deeper checklist, findings taxonomy, and output schema mapping. +> Composition audit is performed as part of `usd-structure-assessment` SA Stage 1; this reference holds the deeper checklist, findings taxonomy, and output schema mapping. --- @@ -11,7 +11,7 @@ Audit the composed stage and authored layers before any processor changes USD content, so downstream optimization can choose safe edit targets and understand composition risks. -This is invoked as a section of `usd-structure-assessment` Phase 1 (composition inventory, asset inventory) and consulted from `usd-edit-target-planner` and `restructure-flow` when deeper composition detail is needed. +This is invoked as a section of `usd-structure-assessment` SA Stage 1 (composition inventory, asset inventory) and consulted from `usd-edit-target-planner` and `apply-restructure` when deeper composition detail is needed. ## Schema reconciliation @@ -69,7 +69,7 @@ When in doubt, write the SA umbrella shape - the audit-report subset is recovera - Candidate edit targets. - Payloads or variants requiring separate coverage. - Evidence needed before Scene Optimizer handoff. -- **Referenced asset manifest** - a list of unique asset layer paths that contain geometry or material data via references or payloads. Downstream skills (`usd-edit-target-planner`, `restructure-flow`, Scene Optimizer handoff) need this list to plan per-asset optimization. +- **Referenced asset manifest** - a list of unique asset layer paths that contain geometry or material data via references or payloads. Downstream skills (`usd-edit-target-planner`, `apply-restructure`, Scene Optimizer handoff) need this list to plan per-asset optimization. ## Output diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/instancing-readiness/references/instancing-tradeoffs.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/instancing-readiness/references/instancing-tradeoffs.md index 5bb6ea8c..1f229f9a 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/instancing-readiness/references/instancing-tradeoffs.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/instancing-readiness/references/instancing-tradeoffs.md @@ -15,7 +15,7 @@ This reference is consulted by: - `usd-hierarchy-dedupe-candidates` when choosing between hierarchy dedupe vs unscoped mesh dedupe. - `instancing-readiness` when explaining merge safety to the user before authoring `instanceable=true`. -- `restructure-flow` when planning Phase 2f restructure orchestration. +- `apply-restructure` when planning Phase 2f restructure orchestration. - `so-interpret-validators` when recommending merge or dedupe ops based on validator findings. ## Prerequisites diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/restructure-decision/README.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/restructure-decision/README.md index 36c61d7d..9aeabfca 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/restructure-decision/README.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/restructure-decision/README.md @@ -64,7 +64,7 @@ The agent assembles a decision packet from prior phases: |---|---|---| | SA classification | `usd-structure-assessment` Phase 2a | Monolithic vs composed; restructure recommended? | | Asset-boundary candidates | `usd-structure-assessment` §2.7 + `usd-hierarchy-dedupe-candidates` | Where the cut points are if restructure is chosen | -| Validator findings | Phase 2c `usd-validation-runner` sweep | Whether structural-only fixes would be wasted on a stage about to be restructured | +| Validator findings | Phase 2c `usd-validation-runner` selected probes | Whether structural-only fixes would be wasted on a stage about to be restructured | | Instancing assessment | Phase 2d (read from SA `instancing` field) | Estimated leverage from restructure | | User constraints | session context | Time budget, mutation policy, output policy | @@ -201,7 +201,7 @@ User accepts the existing structure. Skip Phase 2f. Continue to Phase 3 (instanc ### exit -User declines mutation. Skip to Phase 6e and write a diagnosis-only optimization report capturing the SA + validator findings. +User declines mutation. Skip to Phase 6d and write a diagnosis-only optimization report capturing the SA + validator findings. ### jump-to-verify @@ -289,7 +289,7 @@ Record the user's choice in the optimization plan and emit it for downstream pha - If the user picks `decompose-for-selective-loading`, hand off to `apply-restructure` with the selected boundary level and `goal: selective_loading`; do not perform writes from this reference. -- If the user picks `exit`, immediately go to Phase 6e (`optimization-report`) - do not silently continue to Phase 3. +- If the user picks `exit`, immediately go to Phase 6d (`optimization-report`) - do not silently continue to Phase 3. ## Limitations diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/scripts/audit-report.schema.json b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/scripts/audit-report.schema.json index 8ece9182..da95f8b9 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/scripts/audit-report.schema.json +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/scripts/audit-report.schema.json @@ -2,7 +2,7 @@ "$comment": "SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\nSPDX-License-Identifier: Apache-2.0", "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "USD Performance Tuning Composition Audit Report", - "description": "Composition sub-shape of the umbrella usd-structure-assessment-report.schema.json. Covers the composition slice only (detailed arrays). The umbrella schema uses counts for routing; this schema provides the full arc lists for tools that need them.", + "description": "Composition sub-shape of the umbrella usd-structure-assessment-report.schema.json. Covers the composition slice only (detailed arrays). The umbrella schema uses counts for routing; this schema provides the full arc lists for tools that need them. Casing note: this standalone sub-shape intentionally keeps camelCase keys (e.g. usedLayers, instanceablePrims, unresolvedAssetPaths, recommendedNextActions) to preserve compatibility with external tools/pipelines that consume the composition slice in isolation. The umbrella SA report is itself mixed-case (snake_case SA-domain fields alongside camelCase USD-native stage keys like rootLayer/upAxis/metersPerUnit); do not rename these keys without coordinating with those standalone consumers.", "type": "object", "required": ["schemaVersion", "stage", "composition", "findings", "recommendedNextActions"], "properties": { diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/scripts/usd-structure-assessment-report.schema.json b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/scripts/usd-structure-assessment-report.schema.json index 762555aa..93eab3a2 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/scripts/usd-structure-assessment-report.schema.json +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/scripts/usd-structure-assessment-report.schema.json @@ -104,7 +104,7 @@ }, "summary_counts": { "type": "object", - "description": "Routing-critical counts consumed by validation-scoping.md decision tree and restructure-decision. Strict: typos rejected.", + "description": "Routing-critical counts consumed by usd-validation-runner policy and restructure-decision. Strict: typos rejected.", "required": [ "prim_count", "mesh_count", diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/README.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/README.md index fb4d2138..d5d35645 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/README.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/README.md @@ -5,277 +5,557 @@ ## When to Use -Use this reference for validation-only requests or when the performance workflow reaches the validation-routing phase. +Use this reference for validation-only requests or when the performance workflow +reaches Phase 2c, Phase 4d, Phase 6b, or an iteration that needs validation +evidence. ## Instructions -1. Identify whether the request is validation-only or a validation phase inside the optimization workflow. -2. Select the smallest validation stack that can change the user-visible decision or operation plan. -3. Ask before full Asset Validator sweeps or Tier 3 expensive cross-component checks. -4. Route execution to the owning validation reference or skill and preserve evidence paths for later reporting. - +1. Identify whether the request is validation-only or a validation phase inside + the optimization workflow. +2. Use structure assessment and profile evidence before selecting validators. + Do not instantiate a validator engine, import Scene Optimizer validators, or + enumerate/run rules until a selected validation plan exists. +3. Select the smallest validation stack that can change the user-visible + decision or operation plan. +4. Ask before any full Asset Validator sweep, Tier 3 expensive probe, or + expanded iteration scope. +5. Route execution to the owning validation reference or skill and preserve + evidence paths for later reporting. + +## Ownership Boundary + +This runner is the single owner for validation scoping, full-sweep approval, +large-stage thresholds, masked-stage spot-check policy, and selected-probe +planning. Downstream validator references such as +`references/validate-usd-asset-validator.md` consume the scope note and own +runtime invocation details only. ## Pre-flight Checklist -Before running validators, re-read and confirm: +Before running validators, confirm: + +- [ ] The workflow has SA `summary_counts`, `phase_recommendation`, + `validation_scope`, and `flagged_assets` unless this is a direct + validation-only request. +- [ ] The stage is classified for validation planning as small or large using + the thresholds below. +- [ ] The plan names selected rules and probes, why they were selected, why a + full sweep was skipped or approved, and artifact paths. +- [ ] Expensive checks and full sweeps have explicit user approval when needed. +- [ ] Findings will be routed to `so-interpret-validators` for op-chain + construction; do not map findings to ops yourself. -- [ ] `references/validation-scoping.md` — tier selection, phase-aware subsets, - deferred validators, full-sweep approval gate. -- [ ] SA report's `phase_recommendation` and `summary_counts` drive tier selection. -- [ ] Apply `runtime-artifact-token-budget.md` for large CSV/log output. -- [ ] Route findings to `so-interpret-validators` for op-chain construction — - do not map findings to ops yourself. ## Output Format -Return a scoped validation plan or validation summary naming the selected validator stack, skipped expensive checks, approval gates, artifact paths, and findings that affect the optimization plan. +Return a scoped validation plan or validation summary naming the selected +validator stack, selected rules and probes, skipped expensive checks, approval +gates, artifact paths, and findings that affect the optimization plan. + +For Phase 2c, also write a compact scope note matching +`scripts/validation-scope-note.schema.json`. Validators are named by **canonical +concept**, not runtime class name: + +```json +{ + "scope": "targeted", + "concepts": ["primvar_indexability", "geom_duplicates"], + "targets": [ + { "concept": "primvar_indexability", "paths": ["/World/Racks/Rack_A"] }, + { "concept": "geom_duplicates", "mask_paths": ["/World/Racks"] } + ], + "tier_assignments": { "primvar_indexability": 2, "geom_duplicates": 3 }, + "selection_reason": "...", + "full_sweep": { "status": "skipped", "reason": "...", "approved_by_user": false }, + "artifact_paths": ["..."] +} +``` + +The scope note is the input contract for `scripts/usd_validation_executor.py`. ## Purpose -Use this reference whenever a workflow needs to surface USD validity or performance -validator issues. It picks the smallest validation stack that can affect the -optimization plan, records the evidence contract, and routes concrete execution -to the owning skill or reference. +Use this reference whenever a workflow needs to surface USD validity or +performance validator issues. It picks the smallest validation stack that can +affect the optimization plan, records the evidence contract, and routes concrete +execution to the owning skill or reference. -This reference **does not** execute optimization operations and **does not** choose fix strategies. +This reference **does not** execute optimization operations and **does not** +choose fix strategies. -Use this reference as the entry point for validation-only requests such as -"validate this USD", "run Asset Validator", or "show validation issues". For -broad performance diagnosis, slow loading, high memory, low FPS, or "what -should I optimize?", start with -`omniverse-usd-performance-tuning` so structure assessment can scope validation -before expensive validator runs. +For broad performance diagnosis, slow loading, high memory, low FPS, or "what +should I optimize?", start with `omniverse-usd-performance-tuning` so structure +assessment can scope validation before expensive validator runs. For `omniverse://` targets, start with `omniverse-authentication` before this skill attempts runtime probing or stage open. -Tier tables and the JSON plan template live in -`references/validation-scoping.md` under this reference. This router points at that -reference for tier detail rather than duplicating it, which keeps validation -policy portable for future SO/OAV repo splits. - ## Prerequisites - Target stage or asset paths and resolver context. -- Available validator runtime (Omni Asset Validator inside Kit, project-managed AV install, or installed Scene Optimizer APIs). +- Available validator runtime (Omni Asset Validator inside Kit, project-managed + AV install, or installed Scene Optimizer APIs). - Artifact directory for logs, CSV/JSON findings, and provider summaries. - Baseline, waiver, or failure policy for pre/post processing gates. -- (For perf-stack scoping) `usd-structure-assessment` report with `phase_recommendation` and `flagged_assets`. +- For performance-stack scoping: `usd-structure-assessment` report with + `summary_counts`, `phase_recommendation`, `validation_scope`, and + `flagged_assets`. -## Session-start runtime gate (mandatory when this is the entry skill) +## Session-start runtime gate If this reference is the **entry skill** for the user's request (i.e., the agent invoked `/usd-validation-runner` directly rather than through `omniverse-usd-performance-tuning`), run the session-start gate from -`skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/runtime-context-header.md` *Mandatory session-start gate* +`skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/runtime-context-header.md` before any routing. The gate determines `output_path`, checks `/setup-preflight.json`, invokes `setup-usd-performance-tuning` if the preflight is missing, then surfaces Format A + the 4-option -confirmation. Do not pick a validator stack until the user has -confirmed the runtime. +confirmation. Do not pick a validator stack until the user has confirmed the +runtime. -If invoked **downstream of an entry skill that already fired the gate -in the same session**, skip the gate and proceed. +If invoked downstream of an entry skill that already fired the gate in the same +session, skip the gate and proceed. -## Limitations +--- -- Validation depends on tools installed outside this repo. -- Scene Optimizer validation results are valid only after imports and checker classes are verified in the active runtime. -- Validation evidence is not cleanup; auto-fixes require separate change recording. +## Phase 2c Order: Scope Before Code -## Troubleshooting +Phase 2c is **Phase-aware validation scope + selected probes**. It is not a +default validator sweep. -- If `omni_asset_validate` is unavailable, record it as missing rather than fabricating a pass. -- If Scene Optimizer validator imports fail, do not report SO-specific results. -- If the bundled `validator-venv` is slow or lacks dependencies, prefer a Kit or project-managed Asset Validator environment. +Required order: ---- +1. Read Phase 1 profile and `usd-structure-assessment` output. +2. Classify the asset as small or large for validation planning. +3. Build the selected validation plan from `summary_counts`, + `phase_recommendation`, `validation_scope`, and `flagged_assets`. +4. Record the scope note/artifact. +5. Only then run the selected rules or probes. -## Validator Stack Matrix +For monolithic `optimize-as-is`, the original stage remains the optimization +target, but validation still follows this selected-scope policy. A monolithic +target does not authorize a full sweep. -The router picks one or more stacks based on the intent and the structure assessment `phase_recommendation`. Each stack lists the owned reference that describes the concrete validation command; this reference orchestrates the routing. +## Large for Validation Planning -**All validation cost-control policy lives in -`references/validation-scoping.md`.** That reference owns tier tables, -the full-sweep approval gate, deferred-validator rules, spot-check -policy, phase-aware subset selection, and the per-rule timeout pattern. -Read it when building a perf-stack plan. The key principles: +Treat a stage as **large for validation planning** when any condition is true: -- Structure assessment first — never start with a full default AV sweep. -- Full-sweep approval gate — ask before a default rule set on large/unknown assets. -- SO analysis stays in the validation plan regardless of execution path. -- Spot-check before dropping signal — masked-stage with ≥25% mesh coverage. +- Resolved stage/root package size is unknown or `>100 MB`. +- Composed prim count is `>10,000`. +- Mesh count or prototype/proxy mesh contribution is high enough that a + category sweep would traverse substantial geometry. +- The target is customer-scale CAD/BIM/MEP/factory/plant/city content. +- The request is performance optimization rather than formal conformance. -### Pre-Mutation USD Stack +Large-stage behavior: -For "is this asset USD-correct enough to mutate?", run only the checks needed -to protect the requested mutation. +- Do not run a default full-stage Asset Validator or Scene Optimizer rule sweep. +- Ask before full sweep if the user explicitly wants exhaustive validation. +- Prefer minimum-openability, Tier 1 cheap whole-stage stats/probes, targeted + rules, Tier 2/3 subprocess runs with timeouts, or masked-stage + spot checks. +- Record skipped full-sweep rationale in the scope note/artifact. -| Step | Owner | When | -|---|---|---| -| 1 | Inline minimum-openability check | Always before mutation. Confirm the stage opens, root/default prim is valid enough for the task, asset paths resolve, and scale/up-axis metadata is known or recorded as missing. | -| 2 | `references/validate-usd-asset-validator.md` | When executable NVIDIA Omniverse Asset Validator coverage is needed. Scope by targeted rules/categories unless the user explicitly approves a full sweep. | -| 3 | External profile/package validators | Only when the user explicitly requests a domain profile such as SimReady or a package-validation workflow. Do not keep those command references in this package. | +## Full-Sweep Approval Gate -### Performance stack (scoped) +Trigger before any command or API call that enables the default AV rule set, +all registered rules, all categories, or all SO performance validators over the +whole composed stage when any large-stage condition above holds. -For "is this asset performant?" The chain is: scope -> run -> interpret. Tier and phase-aware scoping live in `references/validation-scoping.md`; the router instructs the agent to load that reference and emit a plan, then invoke the downstream tools. +Ask before full sweep and offer: -| Step | Tool / reference | When | -|---|---|---| -| 1 | (Read `references/validation-scoping.md`) | Build the tier plan from the structure assessment's `flagged_assets` + `phase_recommendation`. | -| 2 | `so-run-validators` | Execute the tier plan against the asset. | -| 3 | `so-interpret-validators` | Read artifacts, classify findings (T1/T2/T3), recommend fixes. | +- **Recommended:** minimum-openability + targeted rule/probe checks. +- **Full sweep:** default rule set with explicit timeout and artifact dir. +- **Defer:** skip full sweep until after mutation or a narrower follow-up. -### Phase-aware subset selection +When approved, record `scope: "approved_full_sweep"`, +`approved_by_user: true`, `timeout_seconds`, and artifact paths. If not +approved, do not launch. -Owned by `references/validation-scoping.md` → *Decision Tree*. Document the -chosen subset in the validation plan so Phase 6 re-validation can reproduce -the same scope. +## Validator Tiers ---- +Tiers describe **execution posture**. Which concept is which tier lives only in +`validator-concepts.json` — do not infer or assert a concept's tier here. -## Routing decision +### Tier 1: Cheap Whole-Stage Stats/Probes -For a given request, pick the stack(s) using this table: +Tier 1 registry concepts plus pure profiling probes that are not concepts +(`printStats`, `countVertices`). Safe to run in one batch over the SA-selected +target; not a default AV all-rules sweep. -| Intent | Stacks | -|---|---| -| "Validate this USD before any mutation." | Pre-mutation USD stack: minimum-openability plus targeted Asset Validator coverage when needed. | -| "What's wrong with this asset?" (broad performance ask) | `usd-structure-assessment` first, then Performance stack scoped per the structure assessment. Add pre-mutation USD stack only when the request is about USD validity or mutation safety. | -| "Run perf validators only." | Performance stack only. | -| "Validate the optimized output (Phase 6 re-validation)." | Same stacks that ran in Phase 2c, for a fair before/after comparison. | -| "Validate a SimReady package/profile." | Defer to the external SimReady/Foundation validation workflow; keep only local USD and performance evidence in this package. | +### Tier 2: Targeted Medium Probes -If intent is ambiguous, ask the user before expanding scope - especially before -invoking Tier 3 perf validators or a default full-stage Asset Validator sweep -(both can be very slow and can produce very large logs/reports). +Tier 2 registry concepts, run per flagged asset (or a bounded sample) in +killable subprocesses. -For performance work, the validation output should be a compact -operation-oriented summary, not a raw issue dump. Use -`so-interpret-validators/references/rule-reference.md` for rule-to-operation -mapping instead of maintaining another mapping in this router. +### Tier 3: Expensive Probes (evidence-gated, mandatory when flagged) ---- +Spatial, pairwise, or high-cardinality analysis. The Tier 3 set is exactly the +concepts `validator-concepts.json` marks `tier: 3` — resolve it from the +registry, do not enumerate it here (see the rule at the top of this section). -## Commands +**Tier 3 is not optional.** When structure assessment flags a target for a +Tier 3 concept, running the **scoped probe is required** — it carries signal the +later op plan depends on, and skipping it is how runs miss real optimizations. +What is approval-gated is *cost*, not *coverage*: -`references/validate-usd-asset-validator.md` owns Asset Validator runtime -details. `so-run-validators` owns Scene Optimizer performance-validator -execution. This router keeps only the common direct invocation shape: +- **Scoped probe = default, no approval needed.** Restrict to the flagged + paths/pairs with `paths=` / `Usd.Stage.OpenMasked()` and run in a bounded + subprocess with a timeout. This is the normal Tier 3 path. +- **Full-stage probe = approval-gated.** Only run the un-scoped, whole-stage + version after the full-sweep approval gate. +- **Timeout is a recorded disposition, not a skip.** If the scoped probe times + out, record `timeout_recorded` and retry a masked/standalone sample — do not + drop the target. -Base Asset Validator (used by `validate-usd-asset-validator`). Probe the -selected runtime before using optional output flags; use JSON output when -the selected runtime advertises it, otherwise fall back to CSV. The commands -below are invocation shapes, not default scope choices. A bare full-stage -invocation is allowed only after the full-sweep gate has been approved. +Every flagged Tier 3 target must end in a coverage-ledger disposition (see +**Completion Gate**). "I skipped it because it was expensive" is not a valid +outcome; the valid outcomes are probed (clean or with findings), `user_declined` +after an explicit ask, `timeout_recorded`, or `blocked_validation_runtime`. -```bash -omni_asset_validate --help -omni_asset_validate /path/to/asset.usd \ - --category Geometry \ - --csv-output /path/to/artifacts/issues.csv -``` - -Minimum openability baseline is a USD-open check, not a default Asset -Validator run: +## Completion Gate (coverage ledger) -```python -from pxr import Usd - -stage = Usd.Stage.Open("/path/to/asset.usd") -if stage is None: - raise RuntimeError("stage did not open") -root = stage.GetDefaultPrim() -print({ - "opened": True, - "default_prim": root.GetPath().pathString if root else None, - "up_axis": stage.GetMetadata("upAxis"), - "meters_per_unit": stage.GetMetadata("metersPerUnit"), -}) -``` +`scripts/usd_validation_executor.py` emits a `coverage_ledger` in every +`validation-report.json`. Each flagged `(target, concept)` from the scope note +must appear with a resolved status: +`probed_with_findings | probed_clean | user_declined | timeout_recorded | +blocked_validation_runtime`. `coverage_ledger.complete` is `true` only when no +flagged target is unresolved, and the report `summary.status` is `BLOCKED` +until it is. **Do not advance to the optimization report or declare the +iteration done while the ledger is incomplete.** -For SO-specific validators, follow `so-run-validators`; SO validator rules -auto-register via `@register_rule` decorators when both packages share the -same Python environment. +## Tier Decision Inputs -### Programmatic per-rule API +No schema contains a single `tier` field. Tier selection is policy applied to +the structure-assessment and validator reports: -For one-rule-at-a-time validation (used by the per-rule timeout pattern in -`references/validation-scoping.md`), drive the engine directly: +| Source | Fields | How they affect tier/scoping | +|---|---|---| +| `usd-structure-assessment-report.schema.json` | `phase_recommendation` | Selects the default validation posture: `structuring`, `optimization`, or `already_optimized`. | +| `usd-structure-assessment-report.schema.json` | `summary_counts.prim_count`, `summary_counts.mesh_count`, `summary_counts.prototype_count`, `summary_counts.instance_count`, `summary_counts.reference_count`, `summary_counts.payload_count` | Determines large-stage status and whether Tier 2/3 must run per target, sampled, or not at all. | +| `usd-structure-assessment-report.schema.json` | `validation_scope.per_asset`, `validation_scope.cross_component_pairs`, `validation_scope.skip` | Defines the concrete target set for Tier 2 and Tier 3. | +| `usd-structure-assessment-report.schema.json` | `flagged_assets`, `findings`, `hierarchy_dedupe.recommended`, `hierarchy_dedupe.top_candidates` | Supplies reasons to include targeted Tier 2 probes or to ask for Tier 3 probes. | +| `validator-concepts.json` | `tier`, `cost_class`, `gpu_bound`, `scope_policy` per canonical concept | Single source of truth for a concept's tier and scope. Read it; do not restate tiers elsewhere. | +| `rule-reference.md` | Validator signal → canonical concept → backing op | Interpretation map only (signal to concept to fix op). Carries no tier. | +| `validation-report.schema.json` | `validators[].canonical_name`, `validators[].status`, `validators[].issues`, `summary.errorCount`, `coverage_ledger` | The canonical executor's own report — what ran (by canonical concept and resolved `(module, class_name)` identity) and what was found. Use it to narrow later iterations, not to widen scope silently. | + +Selected validators are named by **canonical concept name** (e.g. +`primvar_indexability`, `geom_duplicates`), defined in +`references/validator-concepts.json`. The canonical executor resolves each +concept to a unique `(module, class_name)` identity at run time. Do not put +runtime class names (`IndexedPrimvarChecker`), operation names, display labels, +or category names (`Geometry`, `Usd:Performance`) in the plan — class names are +not unique across providers and categories are lookup buckets, not approval +scope. The registry's `preferred_provider` decides Scene Optimizer vs Asset +Validator; performance tuning prefers the Scene Optimizer implementation. + +## Phase-Aware Defaults + +| `phase_recommendation` | Default scope | +|---|---| +| `structuring` | Minimum-openability + targeted structural blockers only. Do not validate geometry about to be restructured. | +| `optimization` | Minimum-openability + Tier 1 cheap whole-stage stats/probes + Tier 2 on flagged targets or sample. Tier 3 scoped probes mandatory on flagged targets/pairs; full-stage Tier 3 after approval. | +| `already_optimized` | Minimum-openability + Tier 1 cheap whole-stage stats/probes only; ask before expanding. | +| missing | Run structure assessment first. Do not begin with validators. | + +## Deterministic Selection + +Selection is a **function of structure-assessment evidence, not agent +judgment**. Two runs over the same SA report must select the same concept set, +because disagreement between runs is the variance this runner exists to remove. +Apply the table top-to-bottom; each matched row contributes its concepts. Do not +add concepts that no row selects, and do not drop a concept a row selects. Tier +and scope policy for each concept come from `validator-concepts.json` (the +"Target" column states only the selection granularity, not the tier). + +| SA signal (condition) | Concepts selected | Target | +|---|---|---| +| Always (any `optimization`/`already_optimized` run) | `composition_missing_ref`, `material_path`, `material_dangling_binding`, `texture_bind`, `texture_normalmap` | whole-stage safety gate | +| `phase_recommendation = optimization` | `material_duplicates`, `structure_empty_leaf`, `structure_invisible`, `structure_flat_hierarchy`, `extents_zero`, `perf_small_mesh`, `perf_sparse_mesh`, `perf_rtx_mesh_count`, `perf_redundant_timesamples`, `perf_high_vertex_count` | whole stage | +| Asset posture is CAD / BIM / MEP / converted (e.g. Revit/HOOPS) | `primitive_fit` | per flagged target — **mandatory**, never dropped | +| `flagged_assets[*]` primvar/UV signal | `primvar_indexability`, `primvar_unused` | per flagged asset | +| `flagged_assets[*]` mesh-hygiene signal (welds/degenerate/winding) | `vertex_weld`, `topology_zero_area_faces`, `normals_winding` | per flagged asset | +| `hierarchy_dedupe.recommended` or duplicate-geometry signal | `geom_duplicates` (+ `geom_duplicates_fuzzy` if near-duplicates) | flagged subtree | +| `validation_scope.cross_component_pairs[*]` with `enclosure_opaque: true` | `spatial_occluded` | flagged pair — **mandatory** scoped probe | +| `validation_scope.cross_component_pairs[*]` (routing/overlap) | `spatial_overlapping`, `spatial_coinciding` | flagged pair — **mandatory** scoped probe | +| Target is simulation-ready (physics/Boolean/3D-print), not visualization | `topology_manifold`, `normals_validity` | flagged target | + +If `validation_scope.skip` lists a target, it is excluded from all rows. If no +asset is flagged, only the "Always" + whole-stage rows fire; ask before adding more. + +## Iteration Subtraction + +Re-validation in later iterations is **same-or-narrower by construction**: + +- Start from the previous iteration's selected concept set. +- **Subtract** every `(target, concept)` whose ledger status was `probed_clean` + or that a completed operation resolved. Resolved-clean targets are not + re-probed. +- **Keep** targets that were `probed_with_findings` (re-verify the fix), + `timeout_recorded` (retry masked/standalone), or regressed. +- **Never widen** to new Tier 3 targets/pairs, new concepts no SA row selects, + or full-stage scope without explicit user approval. +- Keep the FIRST pass's baseline metrics; do not re-baseline. + +This makes each pass cheaper and convergent, and guarantees a later run cannot +silently disagree with an earlier one by re-expanding scope. + +## Scoping Rules + +1. Structure assessment is the first filter. Use `summary_counts`, + duplicate-hierarchy candidates, `validation_scope`, and `flagged_assets` to + decide which validators can change the optimization plan. +2. **Which concepts to run is decided by Deterministic Selection above; tier and + scope policy come from `validator-concepts.json`.** This section does not + re-derive selection or tiering. +3. Do not start performance work with a full default AV sweep. +4. Keep SO analysis in the validation workflow. Importing SO validators makes + rules discoverable; it does not authorize running all of them. +5. For cross-component validators, use `Usd.Stage.OpenMasked()` covering only + the flagged pair and dependency closures, or validate standalone target files. +6. Do not run noisy/slow concepts globally in Phase 2c. Any registry concept + that is `gpu_bound`, `cost_class: expensive`, or `stage_dependent` is scoped + to flagged targets/pairs only — never a full-stage default. +7. Category-scoped AV is still a scoped whole-stage traversal for that category. + On large stages, ask before full sweep and prefer masked spot checks or + bounded parallel subprocesses with timeouts. +8. Prefer summaries over issue dumps. Apply + `runtime-artifact-token-budget.md` for CSV/log/summary handling. + +## Selected-Rule Execution Pattern + +Do not use `ValidationEngine()` or +`ValidationEngine(init_rules=True)` unless the user explicitly approved +exhaustive validation. That pattern runs every registered OAV rule plus every SO +validator that auto-registered. + +Execution model: + +- **Tier 1:** run selected cheap whole-stage stats/probes in one batch for the + scoped target. This is not a default all-rules sweep. +- **Tier 2:** run selected rules per target in isolated OS subprocesses with an + explicit wall-clock timeout. Parallelize independent target/rule subprocesses + within resource budget. +- **Tier 3:** ask first, then use the same subprocess pattern on flagged targets + only. +- **Timeout fallback:** if a Tier 2 or Tier 3 rule times out, record a timeout + finding and rerun a masked-stage spot sample or standalone payload/prototype + sample instead of widening to a full sweep. +- **Do not batch Tier 2/3 rules in one engine** unless the target is small and + the user explicitly accepted the risk. One slow C++ rule can dominate or hang + the whole batch, and Python `signal.alarm` or threads may not interrupt it. + +Inside Kit, import `omni.asset_validator.core` instead of +`omni.asset_validator`, but keep the same selected-rule posture. Ask before full +sweep before any copyable pattern that enables default/all rules. + +### Canonical executor (the only supported runner) + +The runner ships a canonical executor at `scripts/usd_validation_executor.py`. +**Call it directly — do not reimplement rule resolution and do not write your +own script.** It resolves each canonical concept to a unique `(module, +class_name)` via `references/validator-concepts.json`, enables exactly those +rule classes (never `init_rules=True`), and opens the stage scoped. It is +fail-closed by contract: unknown concept, ambiguous identity, unregistered rule, +or missing runtime all raise — there is no bare-name lookup and no CLI fallback. +This is what disambiguates the Scene Optimizer `IndexedPrimvarChecker` (fast +triage) from the Asset Validator one (full audit) that share a class name. ```python -import omni.scene.optimizer.validators # auto-registers SO rules -from omni.asset_validator import CategoryRuleRegistry, ValidationEngine -from pxr import Usd - -registry = CategoryRuleRegistry() -rule = next( - rule - for rule in registry.get_rules("Usd:Performance") - if rule.__name__ == "PrimitiveFitChecker" +from usd_validation_executor import ( + load_registry, + validate_concepts, + ValidationRuntimeUnavailable, +) + +registry = load_registry() # references/validator-concepts.json + +# Tier 1: one batch over the SA-selected target. +issues = validate_concepts( + stage_path, + ["material_duplicates", "structure_empty_leaf"], + registry=registry, ) -engine = ValidationEngine(init_rules=False) -engine.enable_rule(rule) -stage = Usd.Stage.Open("path/to/asset.usd") -results = engine.validate(stage) # synchronous -for issue in results.issues(): - ... +# Tier 2 / Tier 3: one concept + target group per bounded subprocess. +issues = validate_concepts( + stage_path, + ["primvar_indexability"], + registry=registry, + mask_paths=["/World/Racks/Rack_A"], +) ``` -Inside Kit, import `omni.asset_validator.core` instead of `omni.asset_validator` (the `.core` submodule is the in-Kit name). The standalone package uses the plain `omni.asset_validator` import. +If the validation runtime cannot be imported, `validate_concepts` raises +`ValidationRuntimeUnavailable`; record `blocked_validation_runtime` in the +coverage ledger rather than fabricating a pass. -Use this API when you need: +Call `validate_concepts` once for a Tier 1 batch. For Tier 2 and Tier 3, run the +whole scope note through `run_scope_note` with the **subprocess** runner so each +concept executes in a killable child process and timeouts become a recorded +disposition rather than a hang: -- Per-rule timeouts on a large stage (see `references/validation-scoping.md` *Per-rule timeout pattern*). -- A single rule's findings without running the full default set. -- Cross-runtime parity checks (same stage, same rule, both Kit and standalone). +```python +from usd_validation_executor import run_scope_note, subprocess_concept_runner + +report = run_scope_note( + stage_path, + scope_note, # validation-scope-note.schema.json + registry=registry, + concept_runner=subprocess_concept_runner(timeout_seconds=120), + phase="baseline", +) +# report["coverage_ledger"]["complete"] gates "done"; timeouts -> timeout_recorded. +``` -Do not route validation through the Scene Optimizer package's bundled -`validator-venv` — it may lack `numpy`, making large-stage validation slow. -Use a project-managed venv with both `omniverse-asset-validator` and the SO -package on PYTHONPATH. +`subprocess_concept_runner` invokes this module as a child (`python +usd_validation_executor.py`, JSON job on stdin) — an internal worker protocol, +not a CLI. The default in-process runner is for Tier 1 only, where a hang is not +a risk. If a concept times out, `run_scope_note` records `timeout_recorded`; +retry that target with `mask_paths` from the spot-check policy below. + +## Masked-Stage Spot Checks + +Use masked-stage spot checks when full-stage or per-target validation is too +expensive but prim-level findings can still change the optimization plan. + +Use spot checks when: + +- Stage is large for validation planning. +- SA can identify representative candidate subtrees. +- Rule set is mostly prim-local geometry/material/schema checks. +- Tier 2 or Tier 3 subprocess validation times out. +- Result is optimization evidence, not formal conformance. + +Sample selection: + +1. Build a cheap whole-stage inventory first: top branches by mesh count, + semantic names, top prototype/fingerprint groups, material-heavy branches, + and instance-heavy branches. +2. Include all SA-flagged targets that may change the operation plan. +3. Cover at least 25% of mesh-bearing content by mesh count, `rtxMeshCount`, or + instance-proxy mesh contribution. If impractical, record why and mark the + result as limited sample evidence. +4. Include high-risk exemplars: largest mesh, deepest hierarchy, + material-heavy mesh, repeated module, top prototype/fingerprint family, and + dominant mesh-bearing semantic classes. +5. Add closure paths needed by the sample, such as material/looks scopes or + shared class/inherit sources. +6. Reject empty samples; if proxy/prototype-aware counts report 0 meshes, + resample instead of reporting "0 findings." + +Label output `scope: "masked_stage_spot_check"` with sampled paths, semantic +tags, mesh coverage percentage, and evidence scope. + +## Post-Restructure / Post-Decompose Validation Strategy + +After `apply-restructure` or `decompose-for-selective-loading` produces an +assembly root plus payload/prototype files, do not open the full composed stage +with all payloads loaded for a blanket validator sweep. + +- **Assembly skeleton:** open with `Usd.Stage.OpenMasked()` excluding payload + prim paths. Run structural validators only: reference resolution, kind + hierarchy, layer structure, defaultPrim, extent hints, assetInfo. +- **Assembly root as optimization target:** if it retains mesh content after + extraction, validate and optimize it like any other target using this policy. +- **Each payload/prototype:** open each file independently with + `Usd.Stage.Open(payload_file)`. Plan validation per target based on that + target's prim/mesh count. +- **Cross-payload pairs:** open with `Usd.Stage.OpenMasked(root, mask)` covering + only the relevant payload subtrees. Run Tier 3 only per flagged pair. + +Each target re-enters this runner independently; approval gates and spot-check +thresholds apply per target, not to the original composed stage. + +## Asset Validator Load Rules + +The Asset Validator's `ComplianceChecker` opens a new stage from the input's +root layer with default `LoadAll` semantics. Caller `StageLoadRules` such as +`LoadNone` are discarded. `StagePopulationMask` is preserved, so +`Usd.Stage.OpenMasked()` is the reliable scoping mechanism. + +Do not rely on `LoadNone` or `stage.Load(specific_path)` for validation +scoping. Use `OpenMasked` or validate standalone payload/prototype files. + +For small/medium stages, use the standard selected validation plan via +`so-run-validators`, but keep the same tier execution model: Tier 1 may batch; +Tier 2 and Tier 3 use bounded subprocesses. + +## Validation Plan Shape + +The plan is the scope note defined by `scripts/validation-scope-note.schema.json` +— there is no separate plan format. **Deterministic Selection** decides which +concepts the note contains; the registry supplies each concept's tier and scope +policy; masked spot-check fields are described under **Masked-Stage Spot +Checks**. + +## Routing Decision ---- +| Intent | Stacks | +|---|---| +| Validate this USD before mutation | Pre-mutation USD stack: minimum-openability plus targeted Asset Validator coverage when needed. | +| Broad performance ask | `usd-structure-assessment` first, then selected performance stack per this runner. Add pre-mutation USD stack only when validity affects mutation safety. | +| Run perf validators only | Performance stack only, selected from SA evidence or the user's explicit target list. | +| Validate optimized output | Same or narrower stacks than Phase 2c for fair comparison unless the user approves expansion. | +| Formal conformance/exhaustive validation | Ask before full sweep, then route through the selected AV/runtime with explicit timeout and artifacts. | -## Required gates +## Required Gates -Pre-processing (before any mutation): +Pre-processing: - Stage opens. - Asset paths resolve. -- Minimum-openability and selected Asset Validator checks complete. +- Minimum-openability and selected checks complete. - Known blocker findings are either fixed or waived. -Post-processing (Phase 6 re-validation): +Post-processing: - Stage opens. - Validation is no worse than baseline unless explicitly accepted. - Generated outputs are recorded. - Processor report and validation report are attached to the optimization plan. ---- - ## Output -Emit `validation-report.json` matching `scripts/validation-report.schema.json`. The report must point to provider artifacts including `issues.csv`, `provider-summary.json`, and `run.log`, and include the chosen tier/phase scoping so Phase 6 can reproduce it. +Emit `validation-report.json` matching `scripts/validation-report.schema.json` +when that report is produced. The report must point to provider artifacts such +as `issues.csv`, `provider-summary.json`, and `run.log`, and include the chosen +phase scoping so Phase 6 and Phase 7 can reproduce or narrow it. + +## Hard Rules + +1. Never run all validators on all assets by default. +2. Never use `ValidationEngine()` or `ValidationEngine(init_rules=True)` after + SO validator registration unless exhaustive validation was approved. +3. Never run Tier 3 without structural evidence from the assessment. +4. When SA flags a Tier 3 target, the **scoped** probe is mandatory and needs no + approval; ask only before the **full-stage** version. Silent omission of a + flagged expensive probe is a defect, not a cost saving. +5. Ask before full sweep on any large stage. +6. Never start a performance workflow with a full default AV sweep. +7. Prefer masked-stage spot checks over dropping validation when full-stage + validation is too expensive. +8. Run Tier 2 and Tier 3 validation through bounded subprocesses; if a rule + times out, record `timeout_recorded` and retry with a masked or standalone + sample — never silently drop the target. +9. Always report what was skipped and why; the user may override. +10. Never declare an iteration done while `coverage_ledger.complete` is `false`. + Every flagged `(target, concept)` must reach a resolved disposition first. ---- - -## Rules +## Troubleshooting -- Validation is evidence, not cleanup by itself. -- Convert findings into candidate operations via - `so-interpret-validators/references/rule-reference.md`; do not hand the - user raw validator jargon as the recommendation. -- Do not auto-fix without recording which layers or files changed. -- Do not continue after structural USD failures unless the plan is diagnosis-only. -- Do not call Scene Optimizer operation skills until validation evidence has been captured or the user has explicitly chosen to proceed without it. -- All cost-control rules (tiers, gates, thresholds) live in - `references/validation-scoping.md`. Do not invent thresholds inline. +- If `omni_asset_validate` is unavailable, record it as missing rather than + fabricating a pass. +- If Scene Optimizer validator imports fail, do not report SO-specific results. +- If the bundled `validator-venv` is slow or lacks dependencies, prefer a Kit or + project-managed Asset Validator environment. +- Named validator unavailable: record the gap and choose the nearest supported + source only when it answers the same scoped question. ## References -- `references/validation-scoping.md` - tier 1/2/3 tables, JSON plan template, scene-aware adjustment, phase-aware subset rules. Read this when building a perf-stack plan. -- `skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-run-validators/references/infrastructure.md` - SO validator infrastructure (programmatic API, REQUIRES_MESH cache, entry-point allow-list, CLI gotchas, libusd alignment). Read this when debugging SO validator setup. -- `skills/omniverse-usd-performance-tuning/references/workflow.md` - canonical 7-phase flow context for where validation sits. +- `references/validate-usd-asset-validator.md` - Asset Validator runtime + invocation details. +- `skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-run-validators/references/infrastructure.md` - SO validator infrastructure. +- `skills/omniverse-usd-performance-tuning/references/workflow.md` - canonical + 7-phase flow context for where validation sits. diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-interpret-validators/README.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-interpret-validators/README.md index 770d4cd4..476ef703 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-interpret-validators/README.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-interpret-validators/README.md @@ -26,7 +26,8 @@ reads. Do not clone the source repo just to read upstream SO guidance. ## Local Responsibilities - Preserve logical milestone name `so-interpret-validators`. -- Use `validation-scoping.md` for tiering, phase-aware subsets, deferred validators, and approval gates. +- Use `usd-validation-runner/README.md` for tiering, phase-aware subsets, + selected-validator execution policy, and approval gates. - Use `rule-reference.md` only for local recommendation routing; upstream owns generic artifact interpretation mechanics. - Apply `runtime-artifact-token-budget.md` for CSV/log handling and route large artifacts through summaries. diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-interpret-validators/references/follow-up-queries.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-interpret-validators/references/follow-up-queries.md index 365cf555..7297935c 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-interpret-validators/references/follow-up-queries.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-interpret-validators/references/follow-up-queries.md @@ -36,11 +36,11 @@ Look up the rule in the *Rule reference*. Then: > `` wraps ``. To apply the fix, invoke the `so-run-operations` > skill (Claude alias: `/so-run-operations --config '[{"operation":"", ...}]'`) > or call the operation directly via the Python bindings after probing the - > selected SO API surface. For the full invocation reference (runtime probe, - > chains via `executeConfig`, JSON pipelines via - > `standalone.execute_commands_from_json`, and - > `acquire_interface().execute_operation` fallback), see - > upstream `usd-optimize/.agents/operations/INVOCATION.md`. For output + > selected SO API surface. For the full invocation reference (runtime probe, + > chains via `executeConfig`, JSON pipelines via + > `standalone.execute_commands_from_json`, and the required + > `ExecutionContext` stage attachment), see + > `skills/omniverse-usd-performance-tuning/references/so-run-operations/references/invocation.md`. For output > Save-vs-Export policy and digitaltwin workspace rules, see > `skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/usd-edit-target-planner/references/output-saving.md`. For > generic multi-op pipelines organized by bottleneck, see upstream @@ -101,9 +101,14 @@ re-run Steps 3 + 4. ### "Only check ``" (before a run) -The validator runner doesn't expose a `--rule` flag. Tell the user we'll run -the full default set and filter the CSV / summarizer output to that rule before -presenting. If they need to skip expensive rules, they can omit -`--include-expensive` (default). +Selecting which rules run is the canonical executor's job, keyed by **canonical +concept** — there is no `--rule` flag and no run-everything-then-filter step. +Map the rule the user named to its canonical concept in +`references/usd-validation-runner/references/validator-concepts.json`, then run +just that concept through the executor — `validate_concepts(stage, [concept])` +for a single target, or `run_scope_note(...)` for a scoped plan (see +`references/usd-validation-runner/README.md`). The executor enables only the +resolved rule class, so expensive rules are never pulled in unless their concept +is explicitly selected. --- diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-interpret-validators/references/rule-reference.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-interpret-validators/references/rule-reference.md index 702b319d..bac1d46b 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-interpret-validators/references/rule-reference.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-interpret-validators/references/rule-reference.md @@ -3,113 +3,107 @@ # Validator Rule Reference -The mapping below is the local source of truth for the digitaltwin workflow's -`Fix tier` and `Operation` columns. Scene Optimizer validator mechanics and -operation docs live upstream in +This table maps a reported **validator signal** to its **canonical concept** +and the **backing operation** that fixes it. It is the interpretation source for +turning findings into op candidates — it is **not** an execution allowlist and +it does **not** publish tiers. + +**Single source of truth.** Validator *identity* (`module` + `class_name`), +*tier*, *scope policy*, and *preferred provider* live only in +`../../../validator-concepts.json` (keyed by canonical concept). Do not restate +tier numbers here or in the runner README; if a tier matters, read it from the +registry. Execution goes through `scripts/usd_validation_executor.py`, which +resolves the canonical concept to a unique registered rule class and fails +closed on anything unknown or ambiguous. Never copy a runtime class name (e.g. +`IndexedPrimvarChecker`) or a category (`Geometry`, `Usd:Performance`) into a +scope note — class names are not unique across providers. + +Scene Optimizer validator mechanics and operation docs live upstream in [usd-optimize](https://github.com/NVIDIA-omniverse/usd-optimize/) and the prebuilt Scene Optimizer package. Resolve guidance from an extracted package -root via `$SCENE_OPTIMIZER_PACKAGE_ROOT`, then `$SO_HOME`. If no package -root exists, download/extract the published `scene_optimizer_core_...release.zip` +root via `$SCENE_OPTIMIZER_PACKAGE_ROOT`, then `$SO_HOME`. If no package root +exists, download/extract the published `scene_optimizer_core_...release.zip` package (direct archive URLs are in `references/upstreams/usd-optimize.md`) or -use the package path, URL, or extracted root supplied by the user. Do not clone -the source repo just to read SO guidance. To verify a rule's backing operation, -inspect upstream -`source/core/python/omni/scene/optimizer/validators/.py`; the -authoritative registered-rule lists live in upstream `_default_rule_classes()` -and `_expensive_rule_classes()`. - -### SceneOptimizer rules (default) - -| Rule | Backing op | Tier | Notes | -|------|-----------|------|-------| -| SceneOptimizerCoincidingGeometryChecker | `findCoincidingGeometry` | T3 | Analysis-only; prefer `deduplicateGeometry` before destructive deletion. Use upstream `.agents/operations/findCoincidingGeometry.md` for mechanics. | -| SceneOptimizerColocatedVerticesChecker | `meshCleanup` | T1 | `meshCleanup` merges colocated vertices. | -| SceneOptimizerDuplicateFacesChecker | `meshCleanup` | T1 | `meshCleanup` removes duplicate faces. | -| SceneOptimizerDuplicateGeometryChecker | `deduplicateGeometry` | T1 | Converts identical meshes to USD instances. | -| SceneOptimizerDuplicateHierarchiesChecker | `usd-hierarchy-dedupe-candidates` + `apply-restructure` | T3 | Structural signal; use the hierarchy candidate finder + restructure gate, not a direct mesh op. | -| SceneOptimizerDuplicateMaterialsChecker | `optimizeMaterials` | T1 | Merges duplicate material definitions. | -| SceneOptimizerEmptyLeafChecker | `pruneLeaves` | T1 | Removes leaf prims with no geometry. | -| SceneOptimizerFlatHierarchiesChecker | `findFlatHierarchies` | T3 | Analysis-only. Fix: `flattenHierarchy` operation. | -| SceneOptimizerFlattenHierarchyChecker | `flattenHierarchy` | T2 | Has params; tune using upstream `.agents/operations/flattenHierarchy.md`. | -| SceneOptimizerFuzzyDuplicateGeometryChecker | `deduplicateGeometry` | T1 | Same op, different threshold. | -| SceneOptimizerIndexedPrimvarChecker | `optimizePrimvars` | T1 | Converts to indexed primvars; may be deferred when the op is pre-selected. See [`validation-scoping.md`](../../validation-scoping.md) -> Deferred. | -| SceneOptimizerInvisiblePrimsChecker | `removePrims` | T2 | Confirm intent before removing — invisible may be deliberate. | -| SceneOptimizerIsolatedVerticesChecker | `meshCleanup` | T1 | `meshCleanup` removes isolated verts. | -| SceneOptimizerMeshDensityChecker | `countVertices` | T2 | Informational; use lossless reducers first, decimate only after the `decimateMeshes` upfront prompt. | -| SceneOptimizerNonManifoldChecker | `meshCleanup` | T2 | Mesh topology may need repair (non-manifold geometry); skip for visualization-only workflows. See [`validation-scoping.md`](../../validation-scoping.md) -> Deferred. | -| SceneOptimizerNormalsChecker | `generateNormals` | T1 | Regenerates missing/invalid normals. | -| SceneOptimizerPrimitiveFitChecker | `fitPrimitives` | T2 | Primitive replacement; use upstream `.agents/operations/fitPrimitives.md` for parameter semantics. | -| SceneOptimizerRedundantTimeSamplesChecker | `optimizeTimeSamples` | T1 | Removes redundant samples on animated attributes. | -| SceneOptimizerRtxMeshCountChecker | `rtxMeshCount` | T2 | Informational threshold check. Reduce mesh count via `deduplicateGeometry` + `flattenHierarchy` + `removeSmallGeometry`. | -| SceneOptimizerSmallMeshChecker | `removeSmallGeometry` | T1 | Removes meshes below a screen-space threshold. | -| SceneOptimizerSparseMeshChecker | `sparseMeshes` | T2 | Tune density thresholds. | -| SceneOptimizerUnusedUVsChecker | `removeUnusedUVs` | T1 | Removes unbound UV sets; may be deferred when the op is pre-selected. See [`validation-scoping.md`](../../validation-scoping.md) -> Deferred. | -| SceneOptimizerWindingsChecker | `meshCleanup` | T1 | Fixes inconsistent face winding. | -| SceneOptimizerZeroAreaFacesChecker | `meshCleanup` | T1 | Removes degenerate faces. | -| SceneOptimizerZeroExtentChecker | `removeSmallGeometry` | T1 | Analysis finds zero-extent meshes; fix removes them. Use `computeExtents` first when the issue is stale metadata. | - -### SceneOptimizer rules (expensive — only present with `--include-expensive`) - -| Rule | Backing op | Tier | Notes | -|------|-----------|------|-------| -| SceneOptimizerOccludedMeshesChecker | `findOccludedMeshes` → `removePrims` | T3 | **Two-step detect→act chain.** Analysis identifies fully-occluded prim paths; feed those paths to `removePrims` for deletion. Runs FIRST in Phase 4 op chain (before meshCleanup, dedupe, decimate). Scoped to SA containment pairs with `enclosure_opaque: true` only — transparent enclosures are excluded. Two-stage user approval: (1) approve analysis cost, (2) approve deletion of discovered internals. | -| SceneOptimizerFindOverlappingMeshesChecker | `findOverlappingMeshes` | T3 | Analysis-only. Fix: review and remove/merge in DCC. | - -### Base asset-validator rules (`omni.asset_validator.DefaultPlugin`) - -The full list lives in the upstream `omniverse-asset-validator` package; we don't -mirror it here. Many base rules detect issues that map cleanly onto a Scene -Optimizer operation — surface the equivalent op so the user has an automated fix -path even when the rule itself is upstream. - -**Stage / metadata (no SO equivalent — manual fix):** - -- `KindChecker`, `DefaultPrimChecker`, `StageMetadataChecker` — stage-metadata - rules. **T3 / manual.** Fix via USD Python API: - `stage.SetDefaultPrim(...)`, `prim.SetMetadata('kind', 'component')`, - `UsdGeom.SetStageUpAxis(...)`, etc. Check the CSV `Suggestion` column. -- `OmniOrphanedPrimChecker`, `OmniDefaultPrimChecker` — Omni-flavored variants. - T3 / manual. -- `LayerSpecChecker` — type/value mismatches in layer specs. T3 / manual. - -**External references (no SO equivalent — manual fix):** - -- `MissingReferenceChecker` — unresolvable references. T3 / manual. Common cause: - asset flattened on another machine with absolute paths. Fix by re-flattening - with the textures available, or rewriting absolute paths to relative. -- `MaterialPathChecker` — `info:mdl:sourceAsset` attributes pointing at missing - files. T3 / manual. Same root cause as `MissingReferenceChecker`. -- `NormalMapTextureChecker` — `UsdUVTexture inputs:file` unresolvable. T3 / manual. +use the package path supplied by the user. To verify a rule's backing +operation, inspect upstream +`source/core/python/omni/scene/optimizer/validators/.py`. + +### Scene Optimizer rules (default) + +| Validator signal | Canonical concept | Backing op | Notes | +|------|------|-----------|-------| +| SceneOptimizerCoincidingGeometryChecker | `spatial_coinciding` | `findCoincidingGeometry` | Analysis-only; prefer `deduplicateGeometry` before destructive deletion. | +| SceneOptimizerColocatedVerticesChecker | `vertex_weld` | `meshCleanup` | Merges colocated vertices. | +| SceneOptimizerDuplicateFacesChecker | `topology_duplicate_faces` | `meshCleanup` | Removes duplicate faces. | +| SceneOptimizerDuplicateGeometryChecker | `geom_duplicates` | `deduplicateGeometry` | Converts identical meshes to USD instances; run per target or sample, never an unbounded whole-stage default. | +| SceneOptimizerDuplicateHierarchiesChecker | _(structural — no mesh concept)_ | `usd-hierarchy-dedupe-candidates` + `apply-restructure` | Use the hierarchy candidate finder + restructure gate, not a direct mesh op. | +| SceneOptimizerDuplicateMaterialsChecker | `material_duplicates` | `optimizeMaterials` | Merges duplicate material definitions. | +| SceneOptimizerEmptyLeafChecker | `structure_empty_leaf` | `pruneLeaves` | Removes leaf prims with no geometry. | +| SceneOptimizerFlatHierarchiesChecker | `structure_flat_hierarchy` | `findFlatHierarchies` → `flattenHierarchy` | Analysis-only signal; fix is the `flattenHierarchy` operation. | +| SceneOptimizerFuzzyDuplicateGeometryChecker | `geom_duplicates_fuzzy` | `deduplicateGeometry` | Same op, different threshold; run per target or sample. | +| SceneOptimizerIndexedPrimvarChecker | `primvar_indexability` | `optimizePrimvars` | Converts to indexed primvars when the result can change the op plan. | +| SceneOptimizerInvisiblePrimsChecker | `structure_invisible` | `removePrims` | Confirm intent before removing — invisibility may be deliberate. | +| SceneOptimizerIsolatedVerticesChecker | `topology_isolated_vertices` | `meshCleanup` | Removes isolated verts. | +| SceneOptimizerMeshDensityChecker | `perf_high_vertex_count` | `countVertices` | Informational; lossless reducers first, `decimateMeshes` only after the upfront tolerance prompt. | +| SceneOptimizerNonManifoldChecker | `topology_manifold` | `meshCleanup` | Skip for visualization-only workflows; run only for simulation-ready intent. | +| SceneOptimizerNormalsChecker | `normals_validity` | `generateNormals` | Regenerates missing/invalid normals; targeted check only. | +| SceneOptimizerPrimitiveFitChecker | `primitive_fit` | `fitPrimitives` | Bounded-loss; requires the tolerance prompt before applying. Highest-value reducer for converted CAD/BIM content. | +| SceneOptimizerRedundantTimeSamplesChecker | `perf_redundant_timesamples` | `optimizeTimeSamples` | Removes redundant samples on animated attributes. | +| SceneOptimizerRtxMeshCountChecker | `perf_rtx_mesh_count` | `rtxMeshCount` | Informational threshold check. Reduce via `deduplicateGeometry` + `flattenHierarchy` + `removeSmallGeometry`. | +| SceneOptimizerSmallMeshChecker | `perf_small_mesh` | `removeSmallGeometry` | Removes meshes below a screen-space threshold. | +| SceneOptimizerSparseMeshChecker | `perf_sparse_mesh` | `sparseMeshes` | Tune density thresholds. | +| SceneOptimizerUnusedUVsChecker | `primvar_unused` | `removeUnusedUVs` | Removes unbound UV sets when the result can change the op plan. | +| SceneOptimizerWindingsChecker | `normals_winding` | `meshCleanup` | Fixes inconsistent face winding. | +| SceneOptimizerZeroAreaFacesChecker | `topology_zero_area_faces` | `meshCleanup` | Removes degenerate faces. | +| SceneOptimizerZeroExtentChecker | `extents_zero` | `removeSmallGeometry` | Fix removes zero-extent meshes. Use `computeExtents` first when the cause is stale metadata. | + +### Scene Optimizer rules (expensive — only present with `--include-expensive`) + +| Validator signal | Canonical concept | Backing op | Notes | +|------|------|-----------|-------| +| SceneOptimizerOccludedMeshesChecker | `spatial_occluded` | `findOccludedMeshes` → `removePrims` | **Two-step detect→act.** Analysis identifies fully-occluded prim paths; feed those to `removePrims`. Runs first in the Phase 4 op chain. Scope to SA containment pairs with `enclosure_opaque: true`. Two-stage approval: (1) analysis cost, (2) deletion. | +| SceneOptimizerFindOverlappingMeshesChecker | `spatial_overlapping` | `findOverlappingMeshes` | Analysis-only. Fix: review and remove/merge in DCC. | + +These expensive concepts are `gpu_bound` and Tier 3 in the registry; they must be +scoped to flagged pairs (`paths=` / `OpenMasked`) and run in bounded +subprocesses — never full-stage by default on large CAD/BIM/MEP assets. + +### Asset Validator (OAV) base rules + +The full list lives in the upstream `omniverse-asset-validator` package; we +mirror only the concepts that participate in the performance workflow. Many base +rules map onto a Scene Optimizer operation — surface the equivalent op so the +user has an automated fix path even when the rule itself is upstream. **Geometry rules with SO operation equivalents:** -| Base rule | Equivalent SO op | Tier | -|-----------|------------------|------| -| `ExtentsChecker` | `computeExtents` | T1 | -| `IndexedPrimvarChecker` | `optimizePrimvars` | T1 | -| `WeldChecker` | `meshCleanup` (welds colocated verts) | T1 | -| `NormalsValidChecker` | `generateNormals` | T1 | -| `ZeroAreaFaceChecker` | `meshCleanup` | T1 | -| `UnusedMeshTopologyChecker` | `meshCleanup` (removes unreferenced points) | T1 | -| `ManifoldChecker` | `meshCleanup` (some topology repairs need DCC work) | T2 | - -**Deferral** (mirrors the SceneOptimizer table above): - -- `IndexedPrimvarChecker` — defer to the post-op pass when `optimizePrimvars` - is pre-selected by the assessment (CAD/BIM); the checker would just confirm - what the assessment already told you. -- `ManifoldChecker` — skip entirely for visualization-only workflows - (digital twins, AEC, rendering). Run only when the user's target is - simulation-ready (physics, Booleans, 3D printing). - -See `usd-validation-runner/references/validation-scoping.md` -> Deferred for -the conditional logic. - -When marking these in the summary table, label the tier as `T1-equiv` / -`T2-equiv` so the user knows the fix is a Scene Optimizer op, not the -validator's own `--fix` (this repo's validators don't ship a `--fix` mode). - -For rules not in this list, treat as **T3 / manual** and surface the CSV -`Suggestion` column verbatim. Don't invent fix commands. +| OAV base rule | Canonical concept | Backing op | Notes | +|-----------|------|------------------|------| +| `ExtentsChecker` | `extents_general` | `computeExtents` | Broader than SO `ZeroExtentChecker`. | +| `IndexedPrimvarChecker` | `primvar_indexability` (oav impl) | `optimizePrimvars` | **OAV variant is the slow full audit.** Registry tiers the OAV implementation higher than the SO triage one; the executor picks the SO impl for performance tuning. | +| `WeldChecker` | `vertex_weld` | `meshCleanup` | Welds colocated verts. | +| `NormalsValidChecker` | `normals_validity` | `generateNormals` | Targeted check only. | +| `ZeroAreaFaceChecker` | `topology_zero_area_faces` | `meshCleanup` | — | +| `UnusedMeshTopologyChecker` | `topology_unused_mesh` | `meshCleanup` | Removes unreferenced points. | +| `ManifoldChecker` | `topology_manifold` | `meshCleanup` | Some topology repairs need DCC work; skip for visualization-only targets. | + +**Stage / metadata / external references (safety gates — manual fix, no SO op):** + +| OAV base rule | Canonical concept | Notes | +|-----------|------|------| +| `KindChecker` | `kind_metadata` | Fix via `prim.SetMetadata('kind', ...)`. | +| `DefaultPrimChecker` | `layout_default_prim` | Fix via `stage.SetDefaultPrim(...)`. | +| `StageMetadataChecker` | `stage_metadata` | Fix via `UsdGeom.SetStageUpAxis(...)`, etc. | +| `LayerSpecChecker` | `layer_spec_health` | Type/value mismatches in layer specs. | +| `MissingReferenceChecker` | `composition_missing_ref` | Unresolvable references — common on assets flattened elsewhere with absolute paths. High-priority gate for conversions. | +| `MaterialPathChecker` | `material_path` | `info:mdl:sourceAsset` pointing at missing files. | +| `NormalMapTextureChecker` | `texture_normalmap` | `UsdUVTexture inputs:file` unresolvable. | + +For OAV-equivalent fixes, label the op as a Scene Optimizer operation (not the +validator's own `--fix` — this repo's validators don't ship a `--fix` mode). + +For any signal not in this list, treat it as a **manual fix** and surface the +CSV `Suggestion` column verbatim. Don't invent fix commands, and don't assign a +tier here — if the concept matters, add it to `validator-concepts.json`. --- diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-run-validators/README.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-run-validators/README.md index 2067bfb3..48c5ee81 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-run-validators/README.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-run-validators/README.md @@ -16,7 +16,7 @@ when a user directly asks to run Scene Optimizer validators on a USD asset. 1. If this is the entry reference, run the local runtime gate and consume `/setup-preflight.json` before validation. -2. Apply `validation-scoping.md`, deferred-validator policy, and explicit +2. Apply `usd-validation-runner/README.md` selected-scope policy, deferred-validator policy, and explicit approval for expensive checks. 3. Apply `runtime-artifact-token-budget.md`; never read full validator CSVs or full `run.log` into context. @@ -55,7 +55,7 @@ reads. Do not clone the source repo just to read upstream SO guidance. - Runtime context gate and `setup-preflight.json` consumption. - `operationsAvailable` and runtime-family awareness from setup. -- Validation scoping, deferred validators, masked-stage spot-check policy, and +- Validation scoping, selected validators, masked-stage spot-check policy, and expensive-check approval gates. - Runtime artifact token budget for CSV/log/summary handling. - Digitaltwin milestone routing into `so-interpret-validators`. diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-run-validators/references/infrastructure.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-run-validators/references/infrastructure.md index ebf90938..d61b5306 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-run-validators/references/infrastructure.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-run-validators/references/infrastructure.md @@ -25,5 +25,5 @@ reads. Do not clone the source repo just to read upstream SO guidance. ## Local Responsibilities -- Local validation scope, phase-aware subsets, and expensive-check gates remain in `validation-scoping.md`. +- Local validation scope, phase-aware subsets, and expensive-check gates remain in `usd-validation-runner/README.md`. - Setup/install references own runtime selection and `setup-preflight.json` writer behavior. diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/validate-usd-asset-validator.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/validate-usd-asset-validator.md index 8e85b1e6..15f843a6 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/validate-usd-asset-validator.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/validate-usd-asset-validator.md @@ -5,331 +5,121 @@ ## Purpose -Use this validation reference when a stage or asset needs executable NVIDIA -Omniverse Asset Validator coverage. This is a validation-only reference: it -reports issues and recommended next steps, but does not apply fixes unless -explicitly requested. +Run the selected NVIDIA Omniverse Asset Validator checks from the +`usd-validation-runner` scope note and summarize findings. This reference owns +runtime invocation only; scoping, approval gates, full-sweep policy, masked +spot-check policy, and large-stage thresholds live in +`../README.md`. ## Prerequisites -- Run `setup-usd-performance-tuning` first to select a Kit or standalone validation runtime. -- Confirm the target is a USD asset path or asset directory. If basic USD - viability is unknown, first perform the runner's minimum-openability check: - stage opens, root/default prim is valid enough for the task, asset paths - resolve, and scale/up-axis metadata is known or recorded as missing. -- Require either a Kit runtime with `omni.asset_validator.core` or a standalone `omniverse-asset-validator` environment. Install or select a runtime before reporting `blocked_missing_dependency`. -- For unknown, customer-scale, or structurally large assets, require explicit - user approval before running the default full-stage Asset Validator rule set. - The default path is minimum-openability plus targeted rules/categories. +- `setup-usd-performance-tuning` has selected a Kit or standalone validation + runtime. +- Minimum USD openability has passed, or the runner explicitly asked this + reference to perform only that runtime check. +- The Phase 2c scope note names selected rules, target paths or masks, + skipped/approved full-sweep status, and artifact paths. +- Runtime artifact handling follows `../../runtime-artifact-token-budget.md`. -## Runtime Selection - -First resolve the runtime with `setup-usd-performance-tuning`. There are two -supported paths: - -- **Kit path**: use the selected Kit/USD Composer/Kit venv runtime and run - Asset Validator through `omni.asset_validator.core` inside that same Kit - process. Do not require `uv` or `omni_asset_validate` on `PATH` for this path. -- **Standalone path**: use a project-managed `omniverse-asset-validator` - environment. If that standalone environment is missing, invoke - `install-asset-validator-standalone`. - -Do not use the Scene Optimizer package's bundled -`validator-venv/bin/omni_asset_validate` as the preferred Asset Validator -runtime. That venv is package-local and has been observed without `numpy`, -which can make validation much slower on large USD stages. Prefer a -project-managed environment that can import both `omniverse-asset-validator` -and the selected Scene Optimizer package. - -Report `blocked_missing_dependency` only when neither path is available and the -missing runtime cannot be installed or selected. - -## Kit Path - -Use this path when setup returned `ready-kit`, when SO validators are needed, or -when validating remote `omniverse://` assets from the same authenticated Kit -runtime. - -> **Standalone is the preferred runtime.** The standalone path is lighter (no -> Kit overhead), deterministic, and achieves the same validator coverage: SO -> validators auto-register via `@register_rule` decorators when both -> `omniverse-asset-validator` and the SO package are importable in the same -> Python environment. Fall back to Kit when standalone is unavailable or when -> render-time profiling is needed (Kit-only capability). - -1. Use the Python launcher selected by setup: - - Classic Windows Kit: `\python.bat` - - Classic Linux Kit: `/python.sh` or `/python` - - Windows Kit venv: `\Scripts\python.exe` - - Linux Kit venv: `/bin/python` -2. Set `OMNI_KIT_ACCEPT_EULA=yes`. -3. Start Kit with `--no-window` and `--enable omni.asset_validator.core`. -4. Validate through `omni.asset_validator.core.ValidationEngine`. - -Minimal Kit pattern: - -```python -import os - -os.environ.setdefault("OMNI_KIT_ACCEPT_EULA", "yes") - -try: - from kit_app import KitApp -except ImportError: - from omni.kit_app import KitApp - -app = KitApp() -app.startup(["--no-window", "--enable", "omni.asset_validator.core"]) - -from omni.asset_validator.core import ValidationEngine - -asset_abs = "/path/to/asset.usd" -engine = ValidationEngine() -engine.disable_all_rules() -engine.enable_rules("omni.asset_validator.DefaultPlugin.StageMetadataChecker") -results = engine.validate(asset_abs) -issues = results.issues() if callable(results.issues) else results.issues - -print("ASSET_VALIDATOR_ISSUES", len(issues)) -app.shutdown() -``` - -If Scene Optimizer performance validators are also required, use -`so-run-validators` after setup verifies `omni.scene.optimizer.core` in the same -Kit runtime. - -## Standalone Path - -Use this path when the user chooses standalone validation or no Kit runtime is -available. Use a project-managed `omniverse-asset-validator` install created or -verified by `install-asset-validator-standalone` in the same venv where the SO -package is on PYTHONPATH. SO validators auto-register via `@register_rule` -decorators: no manual `register_all()` call is needed for rule discovery. For -targeted or category-scoped runs, select registered rule classes with -`CategoryRuleRegistry` and enable them with `ValidationEngine(init_rules=False)` -plus `enable_rule()`. +## Workflow -Do not use the SO bundled `validator-venv` — it may lack `numpy` and is -slower on large stages. +1. Use the runtime selected by setup; do not invent or switch runtimes. +2. Probe the selected CLI/API help before choosing flags or output formats. +3. Enable only the rules named in the scope note. +4. Ask before full sweep if the runner scope note does not already record + explicit exhaustive approval. +5. Store raw outputs on disk and write a compact summary before reading results + into context. +6. Feed summarized findings to `so-interpret-validators` and the optimization + report. -## Workflow +## Runtime Selection -1. Confirm the input is a USD asset path or an asset directory. -2. Confirm the minimum-openability check has passed, or run it first when - basic USD viability is unknown. -3. Choose the narrowest Asset Validator scope that answers the request: - targeted rule, targeted category, or explicit full-stage pass. For - performance work, prefer rules/categories that can change the - optimization plan; do not use a full-stage default sweep as the - preliminary baseline. -4. For large assets where full-stage coverage is too expensive but validation - signal is still useful, use the *Masked-Stage Spot Check Pattern* below on - representative prims/subtrees before escalating to a full sweep. -5. If the chosen scope is a default full-stage pass on an unknown, - customer-scale, or structurally large asset, stop and ask for approval. - Name the risk plainly: long runtime, high log/report volume, and possible - repeated issue rows. Offer targeted category/rule checks as the recommended - alternative. -6. Run validation with `validate-usd-asset-validator`. -7. Normalize issues by severity, rule, message, location, and suggested fix - when available. For high-volume findings, summarize by rule and target - class rather than dumping every repeated issue into the user-facing report. -8. If the raw Asset Validator report is large, summarize it with the - *Large Report Summarization* policy below before reading results into - context. -9. Fail the report on Asset Validator errors or failures. -10. Warn on Asset Validator warnings unless the active workflow profile promotes them to failures. -11. Hand off performance-oriented findings to `so-run-validators` / - `so-interpret-validators`. For external domain profiles or package - conformance, route to the owning external workflow rather than adding those - references here. - -## Timeout and Approval Policy - -All validation cost-control policy (full-sweep approval gate, timeout -guidance, spot-check thresholds) lives in -`validation-scoping.md` → *Full-Sweep Approval Gate* and -*Decision Tree*. Apply those rules before any full-stage invocation. - -When a timeout is used, record `timeout_seconds` and `timeout_reason`. If the -validator times out, report `status: TIMEOUT` and keep partial logs/artifacts -for follow-up. - -## Standalone CLI Pattern - -Project-managed standalone environment: - -```bash -omni_asset_validate --help -omni_asset_validate asset.usda --category Geometry --csv-output geometry-report.csv -``` +| Runtime | Use | Notes | +|---|---|---| +| Kit | Setup selected Kit, USD Composer, or a Kit venv; remote `omniverse://` validation; or same-runtime Scene Optimizer validation. | Import `omni.asset_validator.core` inside the selected Kit process. Do not require `uv` or `omni_asset_validate` on `PATH`. | +| Standalone | Setup selected a project-managed `omniverse-asset-validator` environment. | Use the selected Python/CLI. Do not use the Scene Optimizer package's bundled `validator-venv` as the preferred runtime. | -Use only the output flags advertised by the selected runtime. Some versions -or wrappers also support JSON output; when they do not, CSV is the -conservative standalone artifact. +Report `blocked_missing_dependency` only when setup cannot provide either +runtime and the user did not approve installation or selection. -Treat category-scoped CLI invocations as scoped full-stage traversals. On large -or unknown-size assets, apply `validation-scoping.md` first: prefer masked -spot checks, or run each category/rule with an explicit wall-clock budget and -record any timeout. +## Runtime detection (not rule selection) -Do not use a bare default invocation such as -`omni_asset_validate asset.usda --csv-output asset-validator-report.csv` on a -large or unknown asset until the full-sweep approval gate has passed. Do not use -`--fix` unless the user explicitly asks for auto-repair behavior. +`omni_asset_validate --help` may be used to confirm a runtime exists. Do **not** +use the CLI to select which validators run: CLI `--rule` flags take bare names, +which cannot disambiguate the Scene Optimizer and Asset Validator rules that +share a class name. Concept selection and execution always go through the +canonical executor (`scripts/usd_validation_executor.py`), which resolves by +identity. Prefer CSV when JSON output is not advertised by the selected runtime. -## Standalone Python API Pattern +## Kit API Pattern -Use the Python API from the selected standalone environment when structured -issue extraction is needed: +Inside Kit, start Kit with the validation extension enabled, then run via the +executor — do not hand-roll engine setup or enable rules by name: ```python -from omni.asset_validator import ValidationEngine - -engine = ValidationEngine() -results = engine.validate("asset.usda") -issues = results.issues() if callable(results.issues) else results.issues +from usd_validation_executor import validate_concepts, run_scope_note ``` -## Masked-Stage Spot Check Pattern +`validate_concepts` / `run_scope_note` import `omni.asset_validator.core`, +construct the engine with `init_rules=False`, and enable only the resolved rule +classes for the scope note's canonical concepts. Do not construct the engine +with default/all-rule initialization unless exhaustive validation was explicitly +approved. -Policy, sample selection rules, and implementation pattern live in -`validation-scoping.md` → *Masked-Stage Spot Checks*. Use that -reference for the canonical `Usd.Stage.OpenMasked()` pattern, mesh coverage -floor (≥25%), and sample manifest fields. +## Standalone API Pattern -Inside Kit, use `omni.asset_validator.core.ValidationEngine` instead of the -standalone import. For a live Kit stage, pass both root and session layers: +In standalone environments, use the same executor entry points. It imports +`omni.asset_validator.core`, falling back to `omni.asset_validator` if needed, +and fails closed with `ValidationRuntimeUnavailable` when neither is importable. +Concepts come from the scope note; never enable rules by bare name. -```python -default_prim = original_stage.GetDefaultPrim() -if default_prim: - mask.Add(default_prim.GetPath()) -masked_stage = Usd.Stage.OpenMasked(original_stage.GetRootLayer(), original_stage.GetSessionLayer(), mask) -if default_prim and not masked_stage.GetDefaultPrim().IsValid(): - raise RuntimeError(f"masked stage excluded default prim {default_prim.GetPath()}") -``` +## Masks And Load Behavior -## Large Report Summarization - -Apply this policy before reading any Asset Validator JSON, CSV, stdout, or log -artifact into agent context. - -First inspect artifact size (`wc -c` on POSIX, `Get-Item | Select -Length` in PowerShell). Treat the artifact as large when it is over 5 MB, when -it has more than 10,000 CSV rows, or when it was produced by a full-stage pass -over an unknown or customer-scale asset. A 166 MB validation report is always a -large artifact. - -For large artifacts, keep the raw report on disk and create or use a compact -summary first: - -- If a packaged summarizer exists for the selected runtime, run it and read - only its compact JSON output. -- If no summarizer exists, create a temporary stdlib-only script beside the - artifact (for example `/_summarize_asset_validator_report.py`). - The script is a run artifact, not repository content, unless the user - explicitly asks to add tooling. -- For CSV reports, stream rows with Python's `csv.DictReader`; do not load the - whole file or print raw rows. -- For JSON reports, parse in the helper process and emit compact counts. It is - acceptable for the helper process to load JSON if the runtime has enough - memory, because the raw JSON still never enters agent context. If the JSON is - too large or the shape is unknown, emit top-level keys and an error summary, - then ask for a CSV export or narrower rule/category run. - -The compact summary should include: - -- `report_path` and `report_bytes` -- `issue_counts` by severity -- top rules/categories by issue count -- grouped failures by `(rule, severity, message, suggestion)` -- up to 10 example locations per group -- `truncated: true|false` -- parse errors or skipped records, if any - -Read only that compact summary into context. The final user-facing validation -report may include capped examples and artifact paths, but it must not include -the full raw `issues` list when the raw report is large. - -## Categories - -Common categories include: - -- `Basic` -- `Geometry` -- `Layer` -- `Layout` -- `Material` -- `Physics` -- `Other` +When the scope note calls for a representative spot check, use target files or +`Usd.Stage.OpenMasked()`. Preserve the default prim, include material or +relationship closure paths when material rules are selected, and verify the +masked stage still exposes relevant mesh-bearing content. -## Output Report +Do not rely on `LoadNone` as the validator scoping mechanism. See +`../README.md` → `Asset Validator Load Rules`. -Reports should include: - -- `asset_path` -- `validator_skill` -- `validator_tool` -- `passed` -- `categories` -- `rules` -- `issue_counts` -- `issues` -- `timeout_seconds` -- `timeout_reason` -- `warnings` -- `errors` -- `next_step` +## Output Report -## Pass/Fail Policy +Record: -Fail when: +- provider, version, command/API path, and runtime path +- scope, selected rules, target paths, masks, and approvals +- raw artifact paths and compact summary paths +- issue counts grouped by severity and rule; include provider category only as + lookup metadata when the runtime emits it +- failures, warnings, skipped checks, timeouts, and limitations -- the Asset Validator dependency is missing -- the asset cannot be opened by Asset Validator -- any issue has severity `ERROR` or `FAILURE` +Do not paste complete validator rows into the user-facing report. -Warn when: +## Pass/Fail Policy -- issues have severity `WARNING` -- the selected category or rule set is narrower than the requested validation goal -- auto-fix suggestions exist but were not applied +Fail only for tool/runtime failure, unreadable stage, schema violation, or +explicit conformance failure. Performance opportunities are findings, not +command failures. ## Limitations -- This validation reference reports Asset Validator results only; it does not apply `--fix` or repair USD content unless the user explicitly asks for auto-repair behavior. -- Rule coverage depends on the selected Kit or standalone Asset Validator version and installed categories. -- Scene Optimizer performance validators are separate and should run through `so-run-validators` when setup verifies `omni.scene.optimizer.core`. -- Remote `omniverse://` targets require an authenticated Kit runtime; standalone validation is for local paths. -- Masked-stage spot checks are optimization evidence, not a substitute for a - full conformance pass when the user asks for formal validation coverage. - -### Payload Loading Behavior - -The Asset Validator discards `StageLoadRules` and re-opens with `LoadAll` — -all payloads are loaded regardless of caller configuration. `StagePopulationMask` -IS preserved (`OpenMasked` works for scoping). Do not rely on `LoadNone` for -validation scoping; use `OpenMasked` or validate standalone files. - -See `validation-scoping.md` §"LoadRules Behavior With the Asset Validator" for -full consequences and workarounds. +- CLI flags and Python APIs vary by installed runtime/version. +- This reference reports Asset Validator findings only; it does not apply + `--fix` or repair USD content unless the user explicitly asks for auto-repair. +- Scene Optimizer performance validators run through `so-run-validators` when + setup verifies `omni.scene.optimizer.core`. +- Spot checks are optimization evidence, not formal full conformance coverage. ## Troubleshooting -- If neither runtime path is available, run `setup-usd-performance-tuning` again or use `install-asset-validator-standalone` to create a project-managed standalone validator. -- If the SO bundled validator venv is missing dependencies such as `numpy`, select the Kit path or project-managed standalone path and record any fallback limitation. -- If validation times out on a large stage, keep the partial report/log, narrow the scope, or ask the user for an explicit larger time budget. -- If Asset Validator passes but external profile/package conformance is still - needed, hand off to that owning external workflow and keep this package's - report focused on USD/Asset Validator evidence. +- If imports fail, return to setup and select or install a supported runtime. +- If the CLI lacks a desired output flag, use an advertised format. +- If validation stalls, stop at the approved budget, keep partial artifacts, and + narrow the next scope through the runner. ## Next Steps -Use this handoff: - -| Asset intent | Next skill | -|---|---| -| Performance validation needed | `so-run-validators` | -| Findings need operation recommendations | `so-interpret-validators` | -| External profile/package conformance needed | Owning external validation workflow | +Pass compact findings to `so-interpret-validators`. Revalidate same-or-narrower +after mutation unless the user approves expansion. diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/validation-scoping.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/validation-scoping.md deleted file mode 100644 index ac5c0ba6..00000000 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/validation-scoping.md +++ /dev/null @@ -1,542 +0,0 @@ - - - -# Validation Scoping — Canonical Cost Guardrails - -> **This is the single source of truth** for validation cost control in the -> digitaltwin-performance-skill workflow. Other references -> (`usd-validation-runner`, `validate-usd-asset-validator`, `workflow.md`) -> point here for tier detail, approval gates, and scoping rules rather than -> duplicating them. - ---- - -## Decision Tree (read this first) - -``` -┌─ What did structure assessment say? ─────────────────────────────────────┐ -│ │ -│ phase_recommendation = "structuring" │ -│ → Structural validators only (do not validate geometry about to be │ -│ restructured) │ -│ │ -│ phase_recommendation = "optimization" │ -│ → Tier 1: always (full stage) │ -│ → Tier 2: per flagged asset (or 10% sample if budget allows) │ -│ → Tier 3: only on specifically flagged targets, ALWAYS ask first │ -│ │ -│ phase_recommendation = "already_optimized" │ -│ → Tier 1 only; ask user before expanding scope │ -│ │ -│ No structure assessment available │ -│ → Run structure assessment first. Do not begin with validators. │ -│ │ -├─ Is a full-stage Asset Validator sweep requested? ───────────────────────┤ -│ │ -│ Trigger the FULL-SWEEP APPROVAL GATE (§ below) when ANY of: │ -│ • Stage size is unknown │ -│ • Customer-scale factory/CAD/BIM/MEP/plant/city │ -│ • SA reports large prim/mesh/prototype/material counts │ -│ • Request is performance triage (not formal conformance) │ -│ │ -│ Offer: targeted rules (recommended) | full sweep + timeout | defer │ -│ │ -├─ Is full-stage AV too expensive but signal is still needed? ─────────────┤ -│ │ -│ Use MASKED-STAGE SPOT CHECKS (§ below) │ -│ • ≥25% mesh coverage or explain why not │ -│ • Label as sample evidence, not full-stage evidence │ -│ │ -├─ Is a full-stage category sweep planned on a very large mesh count? ─────┤ -│ │ -│ Do NOT run uncapped Basic/Layer/Geometry/etc. over the whole stage. │ -│ Use masked spot checks, or run each selected category/rule in a │ -│ subprocess with an explicit wall-clock budget and report TIMEOUT rows. │ -│ │ -├─ Stage has >30K prototype meshes and per-rule attribution is needed? ────┤ -│ │ -│ Use PER-RULE TIMEOUT pattern (§ below) │ -│ │ -├─ Validating post-decompose targets (payloads, assembly root)? ───────────┤ -│ │ -│ Each target re-enters THIS tree independently (§ Post-Restructure │ -│ Validation Strategy). Treat each payload/assembly as a fresh validation │ -│ request — tier selection, approval gate, and spot-check threshold apply │ -│ based on THAT target's prim count, not the original composed stage. │ -│ │ -└──────────────────────────────────────────────────────────────────────────┘ -``` - ---- - -## Purpose - -Translate structure assessment findings into a concrete validation plan that -avoids running expensive validators on assets that do not need them. Use this -after `usd-structure-assessment` produces its report and before invoking -`so-run-validators`. - -## Inputs - -- Structure assessment report: `flagged_assets`, `validation_scope`, `phase_recommendation`. -- User time/resource budget and approval posture for expensive checks. -- Goal type: diagnosis-only or diagnosis plus execution. -- Validator names available in the installed runtime. - ---- - -## Validator Tiers - -### Tier 1: Fast (seconds per asset) - -Metadata and traversal only. Safe to run on all assets. - -- `printStats` -- `countVertices` -- `rtxMeshCount` -- `removeSmallGeometry` (analysis mode) -- `findFlatHierarchies` (analysis mode) -- `pruneLeaves` (analysis mode) -- `removePrims` (analysis mode — orphaned overs) - -### Tier 2: Medium (seconds to minutes per asset) - -Per-mesh inspection. Run on flagged assets or all if budget allows. - -- `meshCleanup` (analysis mode) -- `optimizeMaterials` (analysis mode) -- `removeUnusedUVs` (analysis mode) -- `computeExtents` (analysis mode) -- `generateNormals` (analysis mode) - -### Tier 3: Expensive (minutes per asset, minutes to hours cross-component) - -Spatial or pairwise analysis. Run only on specifically flagged assets/pairs. -**Always ask the user before running.** - -- `deduplicateGeometry` (analysis mode) — on repetition-flagged assets -- `findCoincidingGeometry` (analysis mode) — on repetition-flagged pairs -- `findOccludedMeshes` (analysis mode) — on containment-flagged pairs -- `fitPrimitives` (analysis mode) — on outlier-flagged or primitive-like assets -- `sparseMeshes` (analysis mode) — on outlier-flagged assets - -### Deferred to Post-Optimization Pass - -These validators are expensive AND their findings do not influence operation -selection — the corresponding operations already run unconditionally based on -structural signals. Running these first adds cost without changing the plan. - -| Validator | Why deferred | -|---|---| -| `optimizePrimvars` / `IndexedPrimvarChecker` | `removeUnusedUVs` + `optimizePrimvars` already run for CAD/BIM. Checker produces 100K+ warnings (~6 min, ~156 MB) confirming what SA already told you. | -| `SceneOptimizerNonManifoldChecker` / `ManifoldChecker` | Per-edge topology; only relevant for simulation targets. Visualization-only workflows skip entirely. | -| `SceneOptimizerUnusedUVsChecker` | Same signal as IndexedPrimvar — the operation is already selected. | - -**Exception:** If the assessment does NOT indicate these operations should run -(e.g., a non-BIM scene where UV removal is uncertain), DO run the validators — -their findings provide the evidence to justify the operation. - ---- - -## Full-Sweep Approval Gate - -Trigger before any command that enables the default AV rule set over the whole -composed stage when ANY condition holds: - -1. Stage size is unknown. -2. Target is customer-scale factory/CAD/BIM/MEP/plant/city. -3. SA reports large prim/mesh/prototype/material counts, many - payloads/references, or a monolithic root layer. -4. Request is performance triage rather than formal conformance. - -**Offer the user three options:** - -- **Recommended:** minimum-openability + targeted category/rule checks. -- **Full sweep:** default AV rule set with explicit timeout and artifact dir. -- **Defer:** skip AV until after SA or after mutation. - -When approved, record `scope: "full_stage"`, `approved_by_user: true`, -`timeout_seconds`, and artifact paths. If not approved, do not launch. - ---- - -## Scoping Rules - -1. **Structure assessment is the first filter.** Use `summary_counts` plus - duplicate-hierarchy candidates to decide which validators can change the - optimization plan. Do not start with a full default AV sweep. - -2. **Start with cheap, optimization-oriented evidence.** Duplicate - geometry/materials, empty leaves, flat hierarchies, high vertex/mesh count - summaries, unused UVs (when tolerant of noise), small/zero-extent geometry - (when scoped). - -3. **Keep SO analysis in the validation workflow.** Use AV-registered SO - checkers when available; otherwise run equivalent read-only SO analysis via - `so-run-validators` in the same plan. Do not treat it as a separate phase. - -4. **Run Tier 2 per-asset** on: - - All assets flagged by SA (any reason). - - A sample of unflagged assets if budget allows (e.g., 10% random). - -5. **Run Tier 3 only on flagged targets:** - - `deduplicateGeometry`: only `repetition`-flagged. - - `findOccludedMeshes`: only `containment`-flagged pairs. - - `fitPrimitives` / `sparseMeshes`: only `outlier_extent` or - primitive-like. For CAD/BIM/MEP, pipe/duct/conduit/fitting names or high - prototype mesh counts are enough evidence. Heavy existing instancing does - not remove this signal. - -6. **Cross-component validators** require the composed stage with relevant - payloads visible. Use `Usd.Stage.OpenMasked()` with a population mask - covering only the flagged pair (and their dependency closures), OR run - the validator via standalone `Usd.Stage.Open()` on each target file. - Do NOT rely on `LoadNone` + selective `stage.Load()` — the Asset Validator - discards load rules and re-opens with LoadAll (see §"LoadRules Behavior - With the Asset Validator" below). - -7. **If no assets are flagged**: Tier 1 only. Report the scene as likely - well-structured. Ask before running Tier 2-3. - -8. **Defer noisy/slow full-stage rules.** Do not run - `IndexedPrimvarChecker`, `CoincidingGeometryChecker`, `ExtentsChecker`, - `AlmostExtremeExtentChecker`, `ArticulationChecker`, `ColliderChecker`, or - `KindChecker` globally in the preliminary pass. Run them only when the - scope makes their output actionable (physics checks for simulation assets, - coinciding geometry for repetition-suspect groups, etc.). - -9. **Cap selected category sweeps on large stages.** A category-scoped AV run - is still a full-stage traversal for that category. If - `summary_counts.mesh_count` or prototype/proxy mesh contribution is very - large, do not launch uncapped `Basic`, `Layer`, `Geometry`, `Material`, or - multi-category sweeps over the full composed stage. Prefer masked-stage spot - checks. If a category sweep is still needed, run each selected category or - rule in its own subprocess with an explicit wall-clock budget, record - `timeout_seconds` / `timeout_reason`, and treat partial results as scoped - evidence rather than full conformance. - -10. **Prefer summaries over issue dumps.** Summarize findings into target - counts and operation candidates. Apply `runtime-artifact-token-budget.md` - for CSV/log/summary handling. - ---- - -## Scene-Aware Tier Adjustment - -The static tier assignments above are defaults. Adjust based on -`phase_recommendation` and scene characteristics: - -| Scene signal | Adjustment | -|---|---| -| CAD/BIM/MEP, pipe/duct-heavy, many primitive-like prototypes | Promote `fitPrimitives` analysis into first SO pass (even when heavily instanced — instancing skips mesh dedup, not prototype primitive fitting). | -| Well-structured, artistic | Promote `deduplicateGeometry` + `optimizeMaterials`; demote `fitPrimitives`. | -| Instancing ratio >80% | Skip `deduplicateGeometry`. | -| Scene has 0 extents | Always include `computeExtents`. | -| Enclosed building | Scope `findOccludedMeshes` to within-floor/within-discipline, or skip entirely. | - -When `removeUnusedUVs` and `fitPrimitives` both target the same CAD/BIM -content, interpret `fitPrimitives` candidates against the post-UV-cleanup -state; a pre-cleanup `nonconstPrimvarMeshCount` bucket is still a -primitive-fit opportunity. - ---- - -## Masked-Stage Spot Checks - -> **See also:** "Post-Restructure Validation Strategy" below for how to validate -> after `apply-restructure` or `decompose-for-selective-loading`. Masked-stage -> techniques apply within that framework when individual targets (assembly skeleton -> or a single large payload) are themselves too expensive for a full sweep. - -Use when full-stage AV is too expensive but prim-level findings can still -change the optimization plan. This is the preferred alternative to dropping -validation entirely on customer-scale assets. - -### When to use - -- Stage is customer-scale (CAD/BIM/MEP/factory/city, tens of thousands of - prototype meshes). -- SA can identify representative candidate subtrees. -- Rule set is mostly `CheckPrim`-style geometry/material/schema checks. -- Result is optimization evidence, not formal conformance. - -### Sample selection - -1. Build a cheap full-stage inventory first: top branches by mesh count, - semantic names, top prototype/fingerprint groups, material-heavy branches, - instance-heavy branches. -2. Include all SA-flagged targets that may change the operation plan. -3. Cover **≥25% of mesh-bearing content** by mesh count, `rtxMeshCount`, or - instance-proxy mesh contribution. If floor is impractical, record why and - mark result as limited sample evidence. -4. Include high-risk exemplars: largest mesh, deepest hierarchy, - material-heavy mesh, repeated module, top prototype/fingerprint family, - dominant mesh-bearing semantic classes (pipe/duct/conduit/fitting for MEP; - equivalent for other scenes). -5. Do not sample bare generated prototype roots unless the masked stage is - verified to expose descendant meshes. - -### Implementation - -```python -from pxr import Sdf, Usd, UsdGeom -from omni.asset_validator import CategoryRuleRegistry, ValidationEngine - -asset_abs = "/path/to/customer-scale.usd" -root_layer = Sdf.Layer.FindOrOpen(asset_abs) -default_prim_path = f"/{root_layer.defaultPrim}" if root_layer.defaultPrim else None - -sampled_paths = ["/Root/Prototypes/PipeFamily_A", "/Root/Mechanical/Level_02/AHU_01"] -closure_paths = ["/Root/Looks", "/Root/Materials"] - -mask = Usd.StagePopulationMask() -for path in [*sampled_paths, *closure_paths, default_prim_path]: - if path: - mask.Add(path) - -stage = Usd.Stage.OpenMasked(root_layer, mask) -assert stage.GetDefaultPrim().IsValid(), "masked stage excluded default prim" - -# Verify mesh coverage (proxy/prototype-aware) -mesh_count = sum( - 1 for prim in Usd.PrimRange.Stage( - stage, Usd.TraverseInstanceProxies(Usd.PrimDefaultPredicate) - ) if prim.IsA(UsdGeom.Mesh) -) -# Must be ≥25% of total_scene_mesh_count; raise if 0 - -registry = CategoryRuleRegistry() -normals_rule = next( - rule for rule in registry.get_rules("Geometry") if rule.__name__ == "NormalsValidChecker" -) - -engine = ValidationEngine(init_rules=False) -engine.enable_rule(normals_rule) -results = engine.validate(stage) -``` - -### Constraints - -- Preserve default prim in the mask. -- Add material scopes / relationship targets when running material rules. -- Avoid stage-global / layer-global / dependency rules in spot mode. -- Label output `scope: "masked_stage_spot_check"` with sampled paths, - semantic tags, mesh coverage %, and evidence scope. -- Reject empty samples: if proxy/prototype-aware counts report 0 meshes, - resample instead of reporting "0 findings." - ---- - -## Post-Restructure / Post-Decompose Validation Strategy - -After `apply-restructure` (mode=restructure) or `decompose-for-selective-loading` -produces an assembly root + N payload/prototype files, do NOT open the full composed -stage with all payloads loaded for a blanket validator sweep. That re-couples what was -just decomposed and doubles validation work. - -### Validation Distribution - -- **Assembly skeleton** — open with `Usd.Stage.OpenMasked()` excluding payload prim - paths. Run structural validators only: reference resolution, kind hierarchy, layer - structure, defaultPrim, extent hints, assetInfo. Single pass. - -- **Assembly root as optimization target** — if the assembly root retains mesh - content after extraction (ground planes, shared environment geometry, - non-extracted sub-hierarchies), it is itself a Phase 4 target. Validate and - optimize it like any other target via the decision tree. The manifest records - this with `target_class: "assembly_root"`. - -- **Each payload/prototype (standalone)** — open each file independently with - `Usd.Stage.Open(payload_file)`. Plan validation through the decision tree for - each target (tier selection and approval thresholds apply per-target based on - that target's prim count — a small payload may qualify for spot-check rather - than a full sweep). These are independent and can run in parallel. - -- **Cross-payload pairs** — open with `Usd.Stage.OpenMasked(root, mask)` covering - only the two relevant payload subtrees. Run Tier 3 only (findCoincidingGeometry, - etc.) per flagged pair. - -### Rules - -1. Validate each payload as a standalone file. Do not rely on the assembly context - for per-payload geometry validation. -2. Validate payloads in parallel — they are independent by definition. -3. The assembly-skeleton pass confirms composition integrity (all refs resolve, - no dangling payload arcs, kind/model hierarchy correct). -4. Cross-payload Tier 3 validators use `OpenMasked` to expose only the specific - pair, not all payloads. -5. Report results per-target (assembly vs each payload) so the user sees which - specific payload has issues. -6. Each target in the per-target loop re-enters THIS decision tree as if it were - a fresh validation request — the tree's tier selection, spot-check threshold, - and approval gate apply per-target based on that target's prim count. - -### Why NOT re-compose - -Opening the full composed stage with all payloads loaded for validation after -decompose: -- Defeats the purpose of decomposition (cost is the same as the original). -- Produces per-prim findings attributed to composed paths that don't map cleanly - to individual payload files. -- Prevents parallel validation of independent payloads. -- Triggers the same cost gate the user already approved once (double-prompting). - ---- - -## LoadRules Behavior With the Asset Validator - -The Asset Validator's `ComplianceChecker` opens a **new stage** from -the input's root layer with default `LoadAll` semantics. The original stage's -load rules (e.g. `LoadNone`) are discarded. - -`StagePopulationMask` IS preserved — if the input stage was opened with -`Usd.Stage.OpenMasked()`, the mask carries through to the validator's internal stage. - -This is observable behaviour across current AV versions; do not assume future -releases will behave the same. Probe the AV API/version when possible. - -### Consequences - -- Passing a `LoadNone` stage to the AV is equivalent to passing a `LoadAll` stage. -- All payload content will be loaded and traversed regardless of the load predicate. -- `Usd.PrimRange.Stage(stage, Usd.TraverseInstanceProxies())` visits everything loaded. -- Elapsed time and findings will reflect the full composed stage, not the skeleton. - -### What works - -- `Usd.Stage.OpenMasked()` — physically excludes prims from the stage's view. The AV - preserves the population mask, so masked prims remain invisible to checkers. -- Opening each payload file standalone (preferred — see "Validation Distribution" above). - -### What does NOT work - -- `Usd.Stage.Open(root, Usd.Stage.LoadNone)` — load rules are discarded by AV. -- `stage.Load(specific_path)` after `LoadNone` — same problem, AV re-opens from root. -- Any load-state scoping strategy that relies on `StageLoadRules` being preserved. - ---- - -## Per-Rule Timeout Pattern - -For stages with very high mesh/prototype counts where total validation would -exceed the time budget. Run each category or rule in its own subprocess with an -explicit budget so a single slow checker does not consume the whole pass. - -```python -import subprocess, json, sys -from omni.asset_validator import CategoryRuleRegistry, ValidationEngine -from pxr import Usd - -def validate_one_rule(stage_path, category, rule_name, per_rule_budget_s): - registry = CategoryRuleRegistry() - rule = next( - rule for rule in registry.get_rules(category) if rule.__name__ == rule_name - ) - engine = ValidationEngine(init_rules=False) - engine.enable_rule(rule) - stage = Usd.Stage.Open(stage_path) - results = engine.validate(stage) - return list(results.issues()) - -# Driver: -RULES = [ - ("Geometry", "ManifoldChecker"), - ("Geometry", "WeldChecker"), -] -results = {} -for category, rule_name in RULES: - try: - out = subprocess.run( - [sys.executable, __file__, stage_path, category, rule_name], - timeout=per_rule_budget_s, capture_output=True, check=True, - ) - results[f"{category}.{rule_name}"] = json.loads(out.stdout) - except subprocess.TimeoutExpired: - results[f"{category}.{rule_name}"] = { - "status": "timed-out", - "budget_s": per_rule_budget_s, - } -``` - -**When to use:** >30K prototype meshes, very large `summary_counts.mesh_count`, -suspected single-rule hang, or iterating on one rule without re-running others. -For small/medium stages, use the standard tier plan via `so-run-validators`. - ---- - -## Output — Validation Plan JSON - -```json -{ - "tier1": { - "scope": "full_stage", - "validators": ["printStats", "countVertices", "rtxMeshCount", "..."] - }, - "tier2": { - "scope": "per_asset", - "assets": ["path/to/A.geom.usd", "path/to/B.geom.usd"], - "validators": ["meshCleanup", "optimizePrimvars", "..."] - }, - "tier3": { - "scope": "targeted", - "tasks": [ - { "validator": "findOccludedMeshes", "targets": [["CabinetA", "PipeB"]] }, - { "validator": "deduplicateGeometry", "targets": ["ChairA", "ChairB"] } - ] - }, - "spot_checks": { - "scope": "masked_stage_spot_check", - "sampled_paths": ["", ""], - "mesh_coverage_percent": 28.4, - "evidence_scope": "sample" - }, - "skip": ["list of unflagged assets - no deep validation planned"], - "estimated_time": "fast | minutes | long" -} -``` - -This plan is passed to `so-run-validators` (executor) by `usd-validation-runner` (router). - ---- - -## Hard Rules - -1. Never run all validators on all assets by default. -2. Never run Tier 3 without structural evidence from the assessment. -3. Always ask before Tier 3 cross-component checks. -4. Never start a performance workflow with a full default AV sweep. -5. Prefer masked-stage spot checks over dropping validation when full-stage - is too expensive. -6. Always report what was skipped and why — the user may override. - ---- - -## Duration Hints (typical: ~100K prims, ~200K meshes) - -| Scope | Expected | -|---|---| -| Tier 1 (full stage) | ~5 min | -| Tier 2 (per flagged asset) | ~30 min | -| Tier 3 (cross-component) | hours — always confirm | -| Structural validators only | ~2 min | -| Masked-stage spot check | ~5-10 min | - ---- - -## Prerequisites - -- Structure assessment report (specifically `flagged_assets` and `validation_scope`). -- User time/resource budget. -- Access to validator names in the installed runtime. - -## Limitations - -- Scope quality depends on the structure assessment; weak evidence stays visible. -- Tier cost estimates are defaults — they scale with scene size. -- SO validator names must match the installed runtime. - -## Troubleshooting - -- No assets flagged → Tier 1 only, ask before expanding. -- Tier 3 requested without evidence → return to `usd-structure-assessment`. -- Named validator unavailable → record gap, choose nearest supported source. diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/validator-concepts.json b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/validator-concepts.json new file mode 100644 index 00000000..3817c7cb --- /dev/null +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/validator-concepts.json @@ -0,0 +1,950 @@ +{ + "schema_version": "1.0.0", + "$comment": "Canonical validator-concept registry for the USD performance-tuning workflow. Single binding layer between agent-facing concept names and runtime rule identity, tier, scope policy, and backing op. Curated by maintainers, not inferred. Resolution key is (module, class_name); category is informational. Tiers/costs are CPU-only worst-case from gb300_03 evidence (batch-additivity-findings.md); gpu_bound concepts relax on CUDA hosts. Governed by validator-concepts.schema.json (added in PR-2).", + "concepts": [ + { + "canonical_name": "primvar_indexability", + "display_name": "Indexed primvars", + "role": "opportunity_detector", + "backing_op": "optimizePrimvars", + "tier": 2, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "per_target_or_sample", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.indexed_primvars_checker", + "class_name": "IndexedPrimvarChecker", + "category": "Omni:Geometry", + "use_for": ["performance_tuning"] + }, + { + "provider": "oav", + "module": "omni.asset_validator._geometry_checker", + "class_name": "IndexedPrimvarChecker", + "category": "Geometry", + "tier": 3, + "use_for": ["conformance_audit"] + } + ], + "notes": "Name collision: SO triage wrapper (0.3 s) vs OAV full audit (376 s). Resolve by module; SO is the perf-tuning default." + }, + { + "canonical_name": "primvar_unused", + "display_name": "Unused UV/primvar sets", + "role": "opportunity_detector", + "backing_op": "removeUnusedUVs", + "tier": 2, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "per_target_or_sample", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.unused_uvs_checker", + "class_name": "UnusedUVsChecker", + "category": "Usd:Performance", + "use_for": ["performance_tuning"] + }, + { + "provider": "oav", + "module": "omni.asset_validator._geometry_checker", + "class_name": "UnusedPrimvarChecker", + "category": "Geometry", + "use_for": ["conformance_audit"] + } + ], + "notes": "SO is UV-scoped and ties to removeUnusedUVs; OAV covers all primvars." + }, + { + "canonical_name": "vertex_weld", + "display_name": "Colocated vertices", + "role": "opportunity_detector", + "backing_op": "meshCleanup", + "tier": 2, + "cost_class": "medium", + "gpu_bound": false, + "scope_policy": "per_target_or_sample", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.colocated_vertices_checker", + "class_name": "ColocatedVerticesChecker", + "category": "Omni:Geometry", + "use_for": ["performance_tuning"] + }, + { + "provider": "oav", + "module": "omni.asset_validator._geometry_checker", + "class_name": "WeldChecker", + "category": "Geometry", + "use_for": ["conformance_audit"] + } + ] + }, + { + "canonical_name": "topology_zero_area_faces", + "display_name": "Zero-area faces", + "role": "opportunity_detector", + "backing_op": "meshCleanup", + "tier": 2, + "cost_class": "medium", + "gpu_bound": false, + "scope_policy": "per_target_or_sample", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.zero_area_faces_checker", + "class_name": "ZeroAreaFacesChecker", + "category": "Omni:Geometry", + "use_for": ["performance_tuning"] + }, + { + "provider": "oav", + "module": "omni.asset_validator._geometry_checker", + "class_name": "ZeroAreaFaceChecker", + "category": "Geometry", + "use_for": ["conformance_audit"] + } + ], + "notes": "Class-name singular (OAV) vs plural (SO)." + }, + { + "canonical_name": "topology_manifold", + "display_name": "Non-manifold topology", + "role": "opportunity_detector", + "backing_op": "meshCleanup", + "tier": 3, + "cost_class": "stage_dependent", + "gpu_bound": false, + "scope_policy": "per_target_or_sample", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.nonmanifold_checker", + "class_name": "NonManifoldChecker", + "category": "Omni:Geometry", + "use_for": ["performance_tuning"] + }, + { + "provider": "oav", + "module": "omni.asset_validator._geometry_checker", + "class_name": "ManifoldChecker", + "category": "Geometry", + "use_for": ["conformance_audit"] + } + ], + "notes": "Skip for visualization-only targets; run only for simulation-ready intent. Inverse polarity between impls. Measure before promoting off Tier 3." + }, + { + "canonical_name": "normals_validity", + "display_name": "Missing/invalid normals", + "role": "opportunity_detector", + "backing_op": "generateNormals", + "tier": 3, + "cost_class": "medium", + "gpu_bound": false, + "scope_policy": "per_target_or_sample", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.normals_checker", + "class_name": "NormalsChecker", + "category": "Usd:Performance", + "use_for": ["performance_tuning"] + }, + { + "provider": "oav", + "module": "omni.asset_validator._geometry_checker", + "class_name": "NormalsValidChecker", + "category": "Geometry", + "use_for": ["conformance_audit"] + } + ], + "notes": "Run as Tier 3 only when normals quality is relevant enough to ask for a targeted check." + }, + { + "canonical_name": "normals_winding", + "display_name": "Inconsistent face winding", + "role": "opportunity_detector", + "backing_op": "meshCleanup", + "tier": 2, + "cost_class": "medium", + "gpu_bound": false, + "scope_policy": "per_target_or_sample", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.windings_checker", + "class_name": "WindingsChecker", + "category": "Usd:Performance", + "use_for": ["performance_tuning"] + }, + { + "provider": "oav", + "module": "omni.asset_validator._geometry_checker", + "class_name": "NormalsWindingsChecker", + "category": "Geometry", + "use_for": ["conformance_audit"] + } + ] + }, + { + "canonical_name": "primitive_fit", + "display_name": "Primitive-fittable meshes", + "role": "opportunity_detector", + "backing_op": "fitPrimitives", + "tier": 2, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "per_target_or_sample", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.primitive_fit_checker", + "class_name": "PrimitiveFitChecker", + "use_for": ["performance_tuning"] + } + ], + "parameter_prerequisite": { + "op": "fitPrimitives", + "param": "tolerance", + "question": "What primitive-fit tolerance is acceptable (max deviation from the original mesh)?" + }, + "notes": "Mandatory default opportunity_detector for CAD/BIM/MEP optimization posture. Highest-value reduction for HOOPS-tessellated converted content. Bounded-loss op gated by tolerance." + }, + { + "canonical_name": "geom_duplicates", + "display_name": "Duplicate geometry (exact)", + "role": "opportunity_detector", + "backing_op": "deduplicateGeometry", + "tier": 3, + "cost_class": "stage_dependent", + "gpu_bound": false, + "scope_policy": "per_target_or_sample", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.duplicate_geometry_checker", + "class_name": "DuplicateGeometryChecker", + "category": "Usd:Performance", + "use_for": ["performance_tuning"] + } + ], + "notes": "2.5 s on gb300 (hash short-circuit) but can be minutes with many similar meshes. Treat as Tier 3 default; promote to Tier 2 only after measuring on target stage." + }, + { + "canonical_name": "geom_duplicates_fuzzy", + "display_name": "Duplicate geometry (fuzzy)", + "role": "opportunity_detector", + "backing_op": "deduplicateGeometry", + "tier": 3, + "cost_class": "stage_dependent", + "gpu_bound": false, + "scope_policy": "per_target_or_sample", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.duplicate_geometry_fuzzy_checker", + "class_name": "FuzzyDuplicateGeometryChecker", + "category": "Usd:Performance", + "use_for": ["performance_tuning"] + } + ], + "notes": "Point-cloud distance comparison; can scale poorly. Same backing op, different threshold." + }, + { + "canonical_name": "material_duplicates", + "display_name": "Duplicate materials", + "role": "opportunity_detector", + "backing_op": "optimizeMaterials", + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.duplicate_materials_checker", + "class_name": "DuplicateMaterialsChecker", + "use_for": ["performance_tuning"] + } + ], + "notes": "Material-network hash. High value on converted content where materials are duplicated per instance." + }, + { + "canonical_name": "topology_duplicate_faces", + "display_name": "Duplicate faces", + "role": "opportunity_detector", + "backing_op": "meshCleanup", + "tier": 2, + "cost_class": "medium", + "gpu_bound": false, + "scope_policy": "per_target_or_sample", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.duplicate_face_checker", + "class_name": "DuplicateFaceChecker", + "use_for": ["performance_tuning"] + } + ] + }, + { + "canonical_name": "topology_isolated_vertices", + "display_name": "Isolated vertices", + "role": "opportunity_detector", + "backing_op": "meshCleanup", + "tier": 2, + "cost_class": "medium", + "gpu_bound": false, + "scope_policy": "per_target_or_sample", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.isolated_vertices_checker", + "class_name": "IsolatedVerticesChecker", + "use_for": ["performance_tuning"] + } + ] + }, + { + "canonical_name": "extents_zero", + "display_name": "Zero-extent meshes", + "role": "opportunity_detector", + "backing_op": "removeSmallGeometry", + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.zero_extent_checker", + "class_name": "ZeroExtentChecker", + "use_for": ["performance_tuning"] + } + ], + "notes": "Use computeExtents first when the cause is stale metadata rather than genuinely empty geometry." + }, + { + "canonical_name": "perf_rtx_mesh_count", + "display_name": "RTX mesh count", + "role": "opportunity_detector", + "backing_op": "rtxMeshCount", + "tier": 1, + "cost_class": "medium", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.rtx_mesh_count_checker", + "class_name": "RtxMeshCountChecker", + "use_for": ["performance_tuning"] + } + ], + "notes": "~94 s on gb300 — slowest Tier 1. Counts meshes through composition." + }, + { + "canonical_name": "perf_small_mesh", + "display_name": "Small meshes", + "role": "opportunity_detector", + "backing_op": "removeSmallGeometry", + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.small_mesh_checker", + "class_name": "SmallMeshChecker", + "use_for": ["performance_tuning"] + } + ] + }, + { + "canonical_name": "perf_sparse_mesh", + "display_name": "Sparse meshes", + "role": "opportunity_detector", + "backing_op": "sparseMeshes", + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.sparse_mesh_checker", + "class_name": "SparseMeshChecker", + "use_for": ["performance_tuning"] + } + ] + }, + { + "canonical_name": "perf_high_vertex_count", + "display_name": "High vertex count", + "role": "opportunity_detector", + "backing_op": "countVertices", + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.high_vertex_count_checker", + "class_name": "HighVertexCountChecker", + "use_for": ["performance_tuning"] + } + ], + "notes": "Informational; lossless reducers first, decimateMeshes only after the upfront tolerance prompt." + }, + { + "canonical_name": "perf_redundant_timesamples", + "display_name": "Redundant time samples", + "role": "opportunity_detector", + "backing_op": "optimizeTimeSamples", + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.redundant_timesamples_checker", + "class_name": "RedundantTimeSamplesChecker", + "use_for": ["performance_tuning"] + } + ] + }, + { + "canonical_name": "structure_empty_leaf", + "display_name": "Empty leaf prims", + "role": "opportunity_detector", + "backing_op": "pruneLeaves", + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.empty_leaf_checker", + "class_name": "EmptyLeafChecker", + "use_for": ["performance_tuning"] + } + ] + }, + { + "canonical_name": "structure_flat_hierarchy", + "display_name": "Flat hierarchies", + "role": "target_scoping", + "backing_op": "flattenHierarchy", + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.flat_hierarchies_checker", + "class_name": "FlatHierarchiesChecker", + "use_for": ["performance_tuning"] + } + ], + "notes": "Analysis-only signal; fix is the flattenHierarchy operation (specialty follow-up)." + }, + { + "canonical_name": "structure_invisible", + "display_name": "Invisible prims", + "role": "opportunity_detector", + "backing_op": "removePrims", + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.invisible_prims_checker", + "class_name": "InvisiblePrimsChecker", + "use_for": ["performance_tuning"] + } + ], + "notes": "Confirm intent before removing; invisibility may be deliberate." + }, + { + "canonical_name": "spatial_occluded", + "display_name": "Occluded (internal) meshes", + "role": "opportunity_detector", + "backing_op": "findOccludedMeshes", + "tier": 3, + "cost_class": "expensive", + "gpu_bound": true, + "scope_policy": "flagged_pairs_only", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.occluded_meshes_checker", + "class_name": "OccludedMeshesChecker", + "use_for": ["performance_tuning"] + } + ], + "notes": "Two-step detect->act: feed occluded paths to removePrims. Scope to SA containment pairs with enclosure_opaque:true. 485 s full-stage on CPU; scope via paths=. Two-stage approval (analysis cost, then deletion)." + }, + { + "canonical_name": "spatial_overlapping", + "display_name": "Overlapping meshes", + "role": "target_scoping", + "backing_op": "findOverlappingMeshes", + "tier": 3, + "cost_class": "expensive", + "gpu_bound": true, + "scope_policy": "flagged_pairs_only", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.find_overlapping_meshes_checker", + "class_name": "FindOverlappingMeshesChecker", + "use_for": ["performance_tuning"] + } + ], + "notes": ">3,600 s full-stage on CPU; paths=-scoped to 100 meshes = 72 s. Analysis-only; fix is review/merge. Mandatory scoped probe when SA flags routing pairs." + }, + { + "canonical_name": "spatial_coinciding", + "display_name": "Coinciding geometry", + "role": "target_scoping", + "backing_op": "findCoincidingGeometry", + "tier": 3, + "cost_class": "expensive", + "gpu_bound": true, + "scope_policy": "flagged_pairs_only", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.coinciding_geometry_checker", + "class_name": "CoincidingGeometryChecker", + "use_for": ["performance_tuning"] + } + ], + "notes": "229 s full-stage on CPU. Prefer deduplicateGeometry before destructive deletion." + }, + { + "canonical_name": "composition_missing_ref", + "display_name": "Missing references", + "role": "safety_gate", + "backing_op": null, + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._base_rules", + "class_name": "MissingReferenceChecker", + "use_for": ["safety_gate", "conformance_audit"] + } + ], + "notes": "Manual fix. Common on converted assets with absolute paths. High-priority safety gate for CAD/BIM conversions." + }, + { + "canonical_name": "extents_general", + "display_name": "Extents conformance", + "role": "opportunity_detector", + "backing_op": "computeExtents", + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._base_rules", + "class_name": "ExtentsChecker", + "use_for": ["performance_tuning", "conformance_audit"] + } + ], + "notes": "Broader than SO ZeroExtentChecker." + }, + { + "canonical_name": "kind_metadata", + "display_name": "Kind metadata", + "role": "safety_gate", + "backing_op": null, + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._base_rules", + "class_name": "KindChecker", + "use_for": ["safety_gate", "conformance_audit"] + } + ], + "notes": "Manual fix via USD API." + }, + { + "canonical_name": "type_metadata", + "display_name": "Prim type metadata", + "role": "safety_gate", + "backing_op": null, + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._base_rules", + "class_name": "TypeChecker", + "use_for": ["safety_gate", "conformance_audit"] + } + ] + }, + { + "canonical_name": "stage_metadata", + "display_name": "Stage metadata", + "role": "safety_gate", + "backing_op": null, + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._base_rules", + "class_name": "StageMetadataChecker", + "use_for": ["safety_gate", "conformance_audit"] + } + ] + }, + { + "canonical_name": "prim_encapsulation", + "display_name": "Prim encapsulation", + "role": "safety_gate", + "backing_op": null, + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._base_rules", + "class_name": "PrimEncapsulationChecker", + "use_for": ["safety_gate", "conformance_audit"] + } + ] + }, + { + "canonical_name": "layout_default_prim", + "display_name": "Default prim", + "role": "safety_gate", + "backing_op": null, + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._layout_checker", + "class_name": "DefaultPrimChecker", + "use_for": ["safety_gate", "conformance_audit"] + } + ] + }, + { + "canonical_name": "layout_dangling_over", + "display_name": "Dangling over prims", + "role": "safety_gate", + "backing_op": null, + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._layout_checker", + "class_name": "DanglingOverPrimChecker", + "use_for": ["safety_gate", "conformance_audit"] + } + ] + }, + { + "canonical_name": "material_path", + "display_name": "Material asset paths", + "role": "safety_gate", + "backing_op": null, + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._material_checker", + "class_name": "MaterialPathChecker", + "use_for": ["safety_gate", "conformance_audit"] + } + ], + "notes": "info:mdl:sourceAsset pointing at missing files. Common on converted assets." + }, + { + "canonical_name": "material_dangling_binding", + "display_name": "Dangling material bindings", + "role": "safety_gate", + "backing_op": null, + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._material_checker", + "class_name": "UsdDanglingMaterialBinding", + "use_for": ["safety_gate", "conformance_audit"] + } + ] + }, + { + "canonical_name": "texture_bind", + "display_name": "Texture asset paths", + "role": "safety_gate", + "backing_op": null, + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._base_rules", + "class_name": "TextureChecker", + "use_for": ["safety_gate", "conformance_audit"] + } + ] + }, + { + "canonical_name": "texture_normalmap", + "display_name": "Normal-map textures", + "role": "safety_gate", + "backing_op": null, + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._base_rules", + "class_name": "NormalMapTextureChecker", + "use_for": ["safety_gate", "conformance_audit"] + } + ] + }, + { + "canonical_name": "layer_spec_health", + "display_name": "Layer spec health", + "role": "safety_gate", + "backing_op": null, + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._layer_checker", + "class_name": "LayerSpecChecker", + "use_for": ["safety_gate", "conformance_audit"] + } + ] + }, + { + "canonical_name": "layer_format_perf", + "display_name": "ASCII layer performance", + "role": "opportunity_detector", + "backing_op": null, + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._layer_checker", + "class_name": "UsdAsciiPerformanceChecker", + "use_for": ["performance_tuning", "conformance_audit"] + } + ], + "notes": "Flags .usda data layers that should be binary for load performance. Manual/structural fix." + }, + { + "canonical_name": "utf8_paths", + "display_name": "UTF-8 prim names", + "role": "safety_gate", + "backing_op": null, + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._utf8_checker", + "class_name": "UnicodeNameChecker", + "use_for": ["safety_gate", "conformance_audit"] + } + ] + }, + { + "canonical_name": "subdivision_scheme", + "display_name": "Subdivision scheme", + "role": "safety_gate", + "backing_op": null, + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._geometry_checker", + "class_name": "SubdivisionSchemeChecker", + "use_for": ["safety_gate", "conformance_audit"] + } + ] + }, + { + "canonical_name": "topology_general", + "display_name": "Topology validity", + "role": "safety_gate", + "backing_op": null, + "tier": 2, + "cost_class": "medium", + "gpu_bound": false, + "scope_policy": "per_target_or_sample", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._geometry_checker", + "class_name": "ValidateTopologyChecker", + "use_for": ["conformance_audit"] + } + ] + }, + { + "canonical_name": "topology_unused_mesh", + "display_name": "Unused mesh points", + "role": "opportunity_detector", + "backing_op": "meshCleanup", + "tier": 2, + "cost_class": "medium", + "gpu_bound": false, + "scope_policy": "per_target_or_sample", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._geometry_checker", + "class_name": "UnusedMeshTopologyChecker", + "category": "Geometry", + "use_for": ["conformance_audit"] + } + ], + "notes": "meshCleanup removes unreferenced points." + }, + { + "canonical_name": "normals_existence", + "display_name": "Normals existence", + "role": "opportunity_detector", + "backing_op": "generateNormals", + "tier": 2, + "cost_class": "medium", + "gpu_bound": false, + "scope_policy": "per_target_or_sample", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._geometry_checker", + "class_name": "NormalsExistChecker", + "use_for": ["conformance_audit"] + } + ] + }, + { + "canonical_name": "physics_rigid_body", + "display_name": "Rigid body schema", + "role": "safety_gate", + "backing_op": null, + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._physics_checker", + "class_name": "RigidBodyChecker", + "use_for": ["conformance_audit"] + } + ], + "notes": "Physics-tagged stages only. Skip for visualization targets. Representative of the physics_* family (collider/joint/articulation/mass) in _physics_checker; class names to be confirmed against the runtime registry before enabling." + } + ] +} diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/scripts/asset-validator-validation-report.schema.json b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/scripts/asset-validator-validation-report.schema.json deleted file mode 100644 index 4ec9a4db..00000000 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/scripts/asset-validator-validation-report.schema.json +++ /dev/null @@ -1,133 +0,0 @@ -{ - "$comment": "SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\nSPDX-License-Identifier: Apache-2.0", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "additionalProperties": false, - "required": [ - "asset_path", - "validator_skill", - "validator_tool", - "passed", - "status", - "command", - "categories", - "rules", - "issue_counts", - "issues", - "warnings", - "errors", - "next_step" - ], - "properties": { - "asset_path": { - "type": "string" - }, - "validator_skill": { - "const": "validate-usd-asset-validator" - }, - "validator_tool": { - "const": "omni_asset_validate" - }, - "passed": { - "type": "boolean" - }, - "status": { - "type": "string", - "enum": ["PASS", "FAIL", "BLOCKED", "ERROR", "UNKNOWN"] - }, - "command": { - "type": "array", - "items": { - "type": "string" - } - }, - "categories": { - "type": "array", - "items": { - "type": "string" - } - }, - "rules": { - "type": "array", - "items": { - "type": "string" - } - }, - "issue_counts": { - "type": "object", - "additionalProperties": { - "type": "integer", - "minimum": 0 - }, - "required": ["ERROR", "FAILURE", "WARNING", "INFO"], - "properties": { - "ERROR": { - "type": "integer", - "minimum": 0 - }, - "FAILURE": { - "type": "integer", - "minimum": 0 - }, - "WARNING": { - "type": "integer", - "minimum": 0 - }, - "INFO": { - "type": "integer", - "minimum": 0 - } - } - }, - "issues": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "required": [ - "rule", - "severity", - "message", - "location", - "requirement", - "suggestion" - ], - "properties": { - "rule": { - "type": "string" - }, - "severity": { - "type": "string" - }, - "message": { - "type": "string" - }, - "location": { - "type": ["string", "null"] - }, - "requirement": { - "type": ["string", "null"] - }, - "suggestion": { - "type": ["string", "null"] - } - } - } - }, - "warnings": { - "type": "array", - "items": { - "type": "string" - } - }, - "errors": { - "type": "array", - "items": { - "type": "string" - } - }, - "next_step": { - "type": "string" - } - } -} diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/scripts/usd_validation_executor.py b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/scripts/usd_validation_executor.py new file mode 100644 index 00000000..e50bbfe4 --- /dev/null +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/scripts/usd_validation_executor.py @@ -0,0 +1,577 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Canonical validation reference executor for the USD performance-tuning skill. + +This is the ONE supported way to run validators inside the skill. Agents call +this API with **canonical concept names** (e.g. ``primvar_indexability``); they +never enumerate rules, guess class names, or shell out to a CLI. + +Why this exists +--------------- +Bare rule names are not unique. ``IndexedPrimvarChecker`` is registered by both +Scene Optimizer (0.3 s triage) and the Asset Validator (376 s full audit). A +name-only lookup picks one by registry order, so the same scope note produces +different work and wildly different runtimes on different hosts. That is the +root cause of "every run finds a different solution and it takes forever." + +Contract (no ambiguity, no fallbacks) +------------------------------------- +1. Identity is ``(module, class_name)``, sourced from ``validator-concepts.json``. + Concept -> implementation -> rule class is resolved by identity, never by + bare name. +2. Resolution is fail-closed: zero matches raises, more than one match raises. + The executor never "best-guesses" a rule. +3. The Python validation runtime is required. If it cannot be imported, the + executor raises ``ValidationRuntimeUnavailable`` and the caller records + ``blocked_validation_runtime`` in the coverage ledger. There is no CLI path. +4. Scoping is mandatory for non-whole-stage policies: callers pass ``paths`` / + ``mask_paths`` and the stage is opened with ``Usd.Stage.OpenMasked()``. + +The validator runtime packages (``omni.asset_validator`` / ``pxr``) only import +inside a Kit/AV environment, so every runtime import is deferred into the +function that needs it. Importing this module is always safe (and unit-testable) +without those packages present. +""" +from __future__ import annotations + +import json +import subprocess +from dataclasses import dataclass +from pathlib import Path +from typing import Any, Callable, Iterable + +#: Every ledger disposition is an explicit "resolved" outcome. The completion +#: gate is satisfied only when every planned (target, concept) has one of these. +RESOLVED_STATUSES = frozenset( + { + "probed_with_findings", + "probed_clean", + "user_declined", + "timeout_recorded", + "blocked_validation_runtime", + } +) + +DEFAULT_REGISTRY_PATH = ( + Path(__file__).resolve().parent.parent / "references" / "validator-concepts.json" +) + + +class ConceptResolutionError(RuntimeError): + """A concept name or its identity could not be resolved unambiguously.""" + + +class ValidationRuntimeUnavailable(RuntimeError): + """The Python validation runtime is not importable. No CLI fallback exists.""" + + +#: Single message for every "runtime missing" condition. Callers record +#: ``blocked_validation_runtime`` in the coverage ledger; there is no CLI path. +_RUNTIME_UNAVAILABLE = ( + "No USD validation runtime (omni.asset_validator[.core]) is importable. " + "Record 'blocked_validation_runtime'; there is no CLI fallback." +) + + +@dataclass(frozen=True) +class ResolvedImplementation: + """A concept resolved to a single runtime rule identity.""" + + canonical_name: str + provider: str + module: str + class_name: str + tier: int + scope_policy: str + backing_op: str | None + gpu_bound: bool + + +# --------------------------------------------------------------------------- # +# Registry # +# --------------------------------------------------------------------------- # +def load_registry(path: str | Path | None = None) -> dict[str, Any]: + """Load and index the canonical validator-concept registry. + + Returns a dict with the raw ``concepts`` list plus a ``by_name`` index. + Raises ``ConceptResolutionError`` if a canonical name is duplicated. + """ + registry_path = Path(path) if path is not None else DEFAULT_REGISTRY_PATH + data = json.loads(Path(registry_path).read_text(encoding="utf-8")) + by_name: dict[str, dict[str, Any]] = {} + for concept in data["concepts"]: + name = concept["canonical_name"] + if name in by_name: + raise ConceptResolutionError(f"Duplicate canonical_name in registry: {name}") + by_name[name] = concept + data["by_name"] = by_name + return data + + +def resolve_implementation( + registry: dict[str, Any], + canonical_name: str, + *, + provider: str | None = None, +) -> ResolvedImplementation: + """Resolve a canonical concept name to a single ``(module, class_name)``. + + ``provider`` defaults to the concept's ``preferred_provider`` (``so`` for + performance tuning). Fail-closed: an unknown concept or a provider with no + implementation raises ``ConceptResolutionError``. + """ + concept = registry.get("by_name", {}).get(canonical_name) + if concept is None: + raise ConceptResolutionError( + f"Unknown concept '{canonical_name}'. Concepts must come from " + f"validator-concepts.json; do not synthesize names." + ) + chosen_provider = provider or concept["preferred_provider"] + impls = [im for im in concept["implementations"] if im["provider"] == chosen_provider] + if not impls: + raise ConceptResolutionError( + f"Concept '{canonical_name}' has no '{chosen_provider}' implementation." + ) + if len(impls) > 1: + raise ConceptResolutionError( + f"Concept '{canonical_name}' has ambiguous '{chosen_provider}' implementations." + ) + impl = impls[0] + return ResolvedImplementation( + canonical_name=canonical_name, + provider=impl["provider"], + module=impl["module"], + class_name=impl["class_name"], + tier=int(impl.get("tier", concept["tier"])), + scope_policy=concept["scope_policy"], + backing_op=concept["backing_op"], + gpu_bound=bool(concept["gpu_bound"]), + ) + + +# --------------------------------------------------------------------------- # +# Runtime # +# --------------------------------------------------------------------------- # +def get_rule_registry() -> Any: + """Return the validator runtime's rule registry, or fail closed. + + Tries the Kit core package first, then the standalone package. Raises + ``ValidationRuntimeUnavailable`` if neither imports — there is no CLI path. + """ + try: + from omni.asset_validator.core import ValidationRulesRegistry # type: ignore + + return ValidationRulesRegistry + except ImportError: + pass + try: + from omni.asset_validator import CategoryRuleRegistry # type: ignore + + return CategoryRuleRegistry() + except ImportError as exc: # pragma: no cover - environment dependent + raise ValidationRuntimeUnavailable(_RUNTIME_UNAVAILABLE) from exc + + +def get_validation_engine_cls() -> Any: + """Return the ``ValidationEngine`` class, or fail closed. + + Kit exposes it at ``omni.asset_validator.core``; the standalone package + exposes it at ``omni.asset_validator`` (no ``.core``). Try both so the same + executor works in either runtime. + """ + try: + from omni.asset_validator.core import ValidationEngine # type: ignore + + return ValidationEngine + except ImportError: + pass + try: + from omni.asset_validator import ValidationEngine # type: ignore + + return ValidationEngine + except ImportError as exc: # pragma: no cover - environment dependent + raise ValidationRuntimeUnavailable(_RUNTIME_UNAVAILABLE) from exc + + +def iter_registered_rules(rule_registry: Any) -> Iterable[type]: + """Yield every registered rule *class* (collision-aware enumeration). + + Identity lives on the class as ``__module__`` and ``__name__``. This adapts + to the differing registry shapes across runtimes but never collapses rules + to bare names. Fail-closed: if no enumeration entry point is found, raises. + """ + # Scene Optimizer registers its rules on import for discovery. + try: + import omni.scene.optimizer.validators # type: ignore # noqa: F401 + except ImportError: # pragma: no cover - environment dependent + pass + + # Known registry shapes, probed by entry point (never collapsed to bare + # names — matching stays identity-based below): + # - ``registered_rules`` Kit core ValidationRulesRegistry (iterable/callable) + # - ``rules_by_name`` older name->rule map + # - ``rules`` OAV 1.18.0 CategoryRuleRegistry (iterable of classes) + # This is a runtime adapter, not a correctness fallback. Extend here only if + # a new runtime exposes another shape, and only with an entry point that + # yields rule classes carrying real ``__module__`` / ``__name__`` identity. + rules = getattr(rule_registry, "registered_rules", None) + if callable(rules): + rules = rules() + if rules is None: + mapping = getattr(rule_registry, "rules_by_name", None) + rules = mapping.values() if isinstance(mapping, dict) else None + if rules is None: + direct = getattr(rule_registry, "rules", None) + if callable(direct): + direct = direct() + if isinstance(direct, dict): + direct = direct.values() + rules = direct + if rules is None: + raise ValidationRuntimeUnavailable( + "Could not enumerate registered rules from the runtime registry; the " + "registry API shape is unrecognized. Record the gap rather than guessing." + ) + + for rule in rules: + rule_cls = getattr(rule, "rule", rule) # unwrap registration wrappers + if isinstance(rule_cls, type): + yield rule_cls + + +def resolve_rule_class(rule_registry: Any, module: str, class_name: str) -> type: + """Resolve ``(module, class_name)`` to exactly one registered rule class. + + This is the collision-safe core. ``IndexedPrimvarChecker`` exists twice by + bare name but is unique by ``(module, __name__)``. Fail-closed on zero or + multiple matches. + """ + matches = [ + rule_cls + for rule_cls in iter_registered_rules(rule_registry) + if rule_cls.__module__ == module and rule_cls.__name__ == class_name + ] + if not matches: + raise ConceptResolutionError( + f"Rule not registered in this runtime: {module}.{class_name}. " + f"Confirm the providing package was imported." + ) + if len(matches) > 1: + raise ConceptResolutionError( + f"Ambiguous rule identity: {module}.{class_name} matched " + f"{len(matches)} registered classes." + ) + return matches[0] + + +# --------------------------------------------------------------------------- # +# Scoped stage open # +# --------------------------------------------------------------------------- # +def open_scoped_stage(stage_path: str, mask_paths: list[str] | None = None) -> Any: + """Open a stage, optionally masked to ``mask_paths`` (+ the default prim). + + ``Usd.Stage.OpenMasked()`` is the only reliable scoping mechanism for the + Asset Validator (it discards caller ``StageLoadRules`` but preserves the + population mask). Rejects an empty masked sample so the caller never reports + a misleading "0 findings". + """ + from pxr import Sdf, Usd, UsdGeom # deferred runtime import + + if not mask_paths: + return Usd.Stage.Open(stage_path) + + root_layer = Sdf.Layer.FindOrOpen(stage_path) + mask = Usd.StagePopulationMask() + default_prim_path = f"/{root_layer.defaultPrim}" if root_layer.defaultPrim else None + for path in [*mask_paths, default_prim_path]: + if path: + mask.Add(path) + + stage = Usd.Stage.OpenMasked(root_layer, mask) + assert stage.GetDefaultPrim().IsValid(), "masked stage excluded default prim" + + mesh_count = sum( + 1 + for prim in Usd.PrimRange.Stage( + stage, Usd.TraverseInstanceProxies(Usd.PrimDefaultPredicate) + ) + if prim.IsA(UsdGeom.Mesh) + ) + if mesh_count == 0: + raise RuntimeError("masked validation sample contains no meshes") + return stage + + +# --------------------------------------------------------------------------- # +# Selected validation # +# --------------------------------------------------------------------------- # +def validate_concepts( + stage_path: str, + concepts: list[str], + *, + registry: dict[str, Any] | None = None, + mask_paths: list[str] | None = None, + provider: str | None = None, +) -> list[Any]: + """Run the named canonical concepts on a (optionally masked) stage. + + Enables exactly the resolved rule classes — never ``init_rules=True`` — so + only the selected concepts execute. Intended to be invoked once per Tier 1 + batch, and from a bounded ``subprocess`` per target for Tier 2 / Tier 3 so + a slow C++ rule can be killed by the parent (see the runner README). + + Returns the list of issues. Resolution failures fail closed; a missing + runtime raises ``ValidationRuntimeUnavailable``. + """ + reg = registry if registry is not None else load_registry() + rule_registry = get_rule_registry() + engine_cls = get_validation_engine_cls() + + engine = engine_cls(init_rules=False) + for canonical_name in concepts: + impl = resolve_implementation(reg, canonical_name, provider=provider) + rule_cls = resolve_rule_class(rule_registry, impl.module, impl.class_name) + engine.enable_rule(rule_cls) + + stage = open_scoped_stage(stage_path, mask_paths) + return list(engine.validate(stage).issues()) + + +# --------------------------------------------------------------------------- # +# Coverage ledger + completion gate # +# --------------------------------------------------------------------------- # +def coverage_complete( + planned_targets: list[dict[str, Any]], + ledger_entries: list[dict[str, Any]], +) -> bool: + """The completion gate. + + Returns True only when every planned ``(target, concept)`` has a ledger + entry with a resolved status. This is what prevents an agent from declaring + victory while a flagged Tier 3 probe was silently skipped: an unresolved + target has no entry, so the gate stays closed and the report's + ``coverage_ledger.complete`` is False. + """ + planned = { + (t["target"], t["concept"]) + for tgt in planned_targets + for t in _expand_target(tgt) + } + covered = { + (e["target"], e["concept"]) + for e in ledger_entries + if e["status"] in RESOLVED_STATUSES + } + return planned.issubset(covered) + + +def _iter_execution_units( + target: dict[str, Any], +) -> Iterable[tuple[dict[str, str], list[str]]]: + """Yield ``(ledger_unit, mask_paths)`` for each concrete path/pair in a target. + + The mask is built **per unit** so every probe is scoped to exactly its own + geometry — and, critically, a ``pairs`` entry contributes *both* prim paths + to the mask. (The earlier code derived the mask from ``paths``/``mask_paths`` + only, so a pairs-only spatial target produced an empty mask and silently ran + the approval-gated full stage while the ledger logged it as a scoped probe.) + + A target with no concrete paths/pairs yields a single whole-stage unit with + an empty mask; ``run_scope_note`` permits that only for ``whole_stage`` + concepts and otherwise fails closed. + """ + concept = target["concept"] + singles = list(target.get("paths", [])) + list(target.get("mask_paths", [])) + pairs = [list(p) for p in target.get("pairs", [])] + produced = False + for path in singles: + produced = True + yield {"target": path, "concept": concept}, [path] + for pair in pairs: + produced = True + yield {"target": "::".join(pair), "concept": concept}, list(pair) + if not produced: + yield {"target": "", "concept": concept}, [] + + +def _expand_target(target: dict[str, Any]) -> Iterable[dict[str, str]]: + """Yield one ``{target, concept}`` per concrete path/pair (ledger identity). + + Shares its expansion with execution via ``_iter_execution_units`` so the + completion gate and the executor can never disagree about what was planned. + """ + for unit, _mask in _iter_execution_units(target): + yield unit + + +def run_scope_note( + stage_path: str, + scope_note: dict[str, Any], + *, + registry: dict[str, Any] | None = None, + concept_runner: Callable[..., list[Any]] | None = None, + phase: str = "baseline", + provider: str | None = None, +) -> dict[str, Any]: + """Execute a scope note tier-by-tier and build a schema-valid report. + + ``concept_runner(stage_path, concept, mask_paths=...) -> issues`` is + injectable so Tier 2/3 work can be wrapped in a killable subprocess (see the + runner README's driver section). The default runs in-process via + ``validate_concepts``; for Tier 2/3 the caller should pass a subprocess + driver so one slow C++ rule cannot hang the batch. + + Each target's disposition is recorded in the coverage ledger: + - issues found -> ``probed_with_findings`` + - clean -> ``probed_clean`` + - subprocess timeout -> ``timeout_recorded`` (retry masked/standalone) + - runtime unavailable -> ``blocked_validation_runtime`` + Resolution failures (unknown/ambiguous concept) are NOT swallowed — they + raise, because they indicate a malformed plan, not a runtime condition. + """ + reg = registry if registry is not None else load_registry() + run = concept_runner if concept_runner is not None else validate_concepts + + validators: list[dict[str, Any]] = [] + ledger: list[dict[str, Any]] = [] + error_count = 0 + + for target in scope_note.get("targets", []): + concept = target["concept"] + impl = resolve_implementation(reg, concept, provider=provider) # fail-closed + base_entry = { + "name": concept, + "kind": "rule", + "canonical_name": concept, + "module": impl.module, + "class_name": impl.class_name, + } + for unit, mask_paths in _iter_execution_units(target): + if impl.scope_policy != "whole_stage" and not mask_paths: + raise ConceptResolutionError( + f"Concept '{concept}' has scope_policy '{impl.scope_policy}' but its " + f"target supplied no paths or pairs to scope to. A scoped concept " + f"must never fall back to an implicit full-stage run; fix the scope " + f"note (provide paths/pairs, or use the approved full-sweep path)." + ) + try: + issues = run(stage_path, [concept], registry=reg, mask_paths=mask_paths) + except subprocess.TimeoutExpired: + validators.append({**base_entry, "status": "TIMEOUT"}) + ledger.append({**unit, "tier": impl.tier, "status": "timeout_recorded"}) + continue + except ValidationRuntimeUnavailable as exc: + validators.append({**base_entry, "status": "BLOCKED", "notes": str(exc)}) + ledger.append({**unit, "tier": impl.tier, "status": "blocked_validation_runtime"}) + continue + count = len(issues) + error_count += count + validators.append({**base_entry, "status": "FAIL" if count else "PASS", "issues": count}) + ledger.append({ + **unit, + "tier": impl.tier, + "status": "probed_with_findings" if count else "probed_clean", + }) + + complete = coverage_complete(scope_note.get("targets", []), ledger) + return { + "schemaVersion": "1.0.0", + "phase": phase, + "stage": {"identifier": stage_path}, + "validators": validators, + "summary": { + "status": "BLOCKED" if not complete else ("FAIL" if error_count else "PASS"), + "errorCount": error_count, + "warningCount": 0, + }, + "coverage_ledger": {"complete": complete, "entries": ledger}, + } + + +# --------------------------------------------------------------------------- # +# Subprocess runner (killable Tier 2 / Tier 3) # +# --------------------------------------------------------------------------- # +def subprocess_concept_runner( + *, + timeout_seconds: int = 120, + python_executable: str | None = None, + registry_path: str | Path | None = None, +) -> Callable[..., list[Any]]: + """Build a ``concept_runner`` that runs each concept in a child process. + + Tier 2 / Tier 3 rules are C++-heavy and can hang; Python ``signal``/threads + cannot interrupt them. Running each concept in a child process means the + parent can kill it on timeout. Pass the returned callable as + ``run_scope_note(..., concept_runner=subprocess_concept_runner())``. + + The child is invoked as ``python `` with a JSON job on stdin and + a JSON result on stdout — an internal worker protocol, not a CLI: there are + no rule-selection flags. On timeout, ``subprocess.TimeoutExpired`` propagates + (``run_scope_note`` records ``timeout_recorded``). A child that reports the + runtime missing raises ``ValidationRuntimeUnavailable``. + """ + import os + import sys + + executable = python_executable or sys.executable + worker = str(Path(__file__).resolve()) + + def _runner(stage_path, concepts, *, registry=None, mask_paths=None): + job = json.dumps( + { + "stage_path": stage_path, + "concept": concepts[0], + "mask_paths": mask_paths or [], + "registry_path": str(registry_path) if registry_path else None, + } + ) + env = dict(os.environ) + env["PYTHONPATH"] = os.pathsep.join( + [str(Path(worker).parent), env.get("PYTHONPATH", "")] + ) + completed = subprocess.run( + [executable, worker], + input=job, + capture_output=True, + text=True, + timeout=timeout_seconds, + env=env, + ) + if completed.returncode != 0: + raise RuntimeError( + f"validation worker failed (rc={completed.returncode}): " + f"{completed.stderr.strip()[:500]}" + ) + result = json.loads(completed.stdout.strip().splitlines()[-1]) + if result.get("status") == "blocked_validation_runtime": + raise ValidationRuntimeUnavailable(result.get("detail", "runtime unavailable")) + return list(result.get("issues", [])) + + return _runner + + +def _worker_main() -> int: + """Child entrypoint: read one JSON job from stdin, print a JSON result. + + Internal protocol used by ``subprocess_concept_runner`` — not a user CLI. + """ + import sys + + job = json.loads(sys.stdin.read()) + registry = load_registry(job.get("registry_path")) + try: + issues = validate_concepts( + job["stage_path"], + [job["concept"]], + registry=registry, + mask_paths=job.get("mask_paths") or None, + ) + except ValidationRuntimeUnavailable as exc: + print(json.dumps({"status": "blocked_validation_runtime", "detail": str(exc)})) + return 0 + print(json.dumps({"status": "ok", "issues": [str(i) for i in issues]})) + return 0 + + +if __name__ == "__main__": + raise SystemExit(_worker_main()) diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/scripts/validation-report.schema.json b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/scripts/validation-report.schema.json index 4dfa32d9..789c2e66 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/scripts/validation-report.schema.json +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/scripts/validation-report.schema.json @@ -2,28 +2,93 @@ "$comment": "SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\nSPDX-License-Identifier: Apache-2.0", "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "USD Performance Tuning Validation Report", + "$comment": "Output of the validation reference executor. Strict/closed. The coverage_ledger is required and machine-enforces the completion gate: the run cannot declare done while a flagged target is unresolved.", "type": "object", - "required": ["schemaVersion", "stage", "validators", "summary", "findings"], + "additionalProperties": false, + "required": ["schemaVersion", "stage", "phase", "validators", "summary", "coverage_ledger"], "properties": { "schemaVersion": { "type": "string" }, + "phase": { "enum": ["baseline", "after"] }, "stage": { "type": "object", + "additionalProperties": false, "required": ["identifier"], "properties": { "identifier": { "type": "string" }, "rootLayer": { "type": "string" } } }, - "validators": { "type": "array", "items": { "type": "object" } }, + "validators": { + "type": "array", + "items": { "$ref": "#/$defs/validator_entry" } + }, "summary": { "type": "object", + "additionalProperties": false, + "required": ["status", "errorCount", "warningCount"], "properties": { - "status": { "type": "string" }, - "errorCount": { "type": "integer" }, - "warningCount": { "type": "integer" } + "status": { "enum": ["PASS", "FAIL", "BLOCKED"] }, + "errorCount": { "type": "integer", "minimum": 0 }, + "warningCount": { "type": "integer", "minimum": 0 } } }, - "findings": { "type": "array", "items": { "type": "object" } } + "coverage_ledger": { "$ref": "#/$defs/coverage_ledger" }, + "findings": { "type": "array", "items": { "type": "object" } }, + "artifacts": { "type": "object" }, + "runtime_context": { "type": "object" }, + "generated_at": { "type": "string" } }, - "additionalProperties": true + "$defs": { + "validator_entry": { + "type": "object", + "additionalProperties": false, + "required": ["name", "kind", "status"], + "properties": { + "name": { "type": "string" }, + "kind": { "enum": ["openability", "rule"] }, + "status": { "enum": ["PASS", "FAIL", "SKIPPED", "TIMEOUT", "BLOCKED"] }, + "canonical_name": { "type": "string", "pattern": "^[a-z][a-z0-9_]*$" }, + "module": { "type": "string" }, + "class_name": { "type": "string" }, + "issues": { "type": "integer", "minimum": 0 }, + "notes": { "type": "string" } + }, + "allOf": [ + { + "$comment": "Rule entries must carry resolved identity — proof the collision-safe resolver ran. Never a bare name.", + "if": { "properties": { "kind": { "const": "rule" } } }, + "then": { "required": ["canonical_name", "module", "class_name"] } + } + ] + }, + "coverage_ledger": { + "type": "object", + "additionalProperties": false, + "required": ["complete", "entries"], + "properties": { + "complete": { + "type": "boolean", + "$comment": "true only when no flagged target is unresolved. The completion gate keys on this field." + }, + "entries": { + "type": "array", + "items": { "$ref": "#/$defs/ledger_entry" } + } + } + }, + "ledger_entry": { + "type": "object", + "additionalProperties": false, + "required": ["target", "concept", "tier", "status"], + "properties": { + "target": { "type": "string", "minLength": 1 }, + "concept": { "type": "string", "pattern": "^[a-z][a-z0-9_]*$" }, + "tier": { "enum": [1, 2, 3] }, + "status": { + "enum": ["probed_with_findings", "probed_clean", "user_declined", "timeout_recorded", "blocked_validation_runtime"] + }, + "reason": { "type": "string" } + } + } + } } diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/scripts/validation-scope-note.schema.json b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/scripts/validation-scope-note.schema.json new file mode 100644 index 00000000..d620a048 --- /dev/null +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/scripts/validation-scope-note.schema.json @@ -0,0 +1,63 @@ +{ + "$comment": "SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\nSPDX-License-Identifier: Apache-2.0", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "USD Validation Scope Note", + "$comment": "Input plan consumed by the validation reference executor. Concepts are canonical names resolved against validator-concepts.json; raw runtime class names are forbidden by pattern.", + "type": "object", + "additionalProperties": false, + "required": ["scope", "concepts", "targets", "tier_assignments", "selection_reason", "artifact_paths"], + "properties": { + "scope": { + "enum": ["minimum_openability", "targeted", "masked_stage_spot_check", "approved_full_sweep", "structural_only"] + }, + "concepts": { + "type": "array", + "minItems": 0, + "items": { + "type": "string", + "pattern": "^[a-z][a-z0-9_]*$", + "$comment": "Canonical concept name; must resolve in validator-concepts.json (CI cross-check)." + } + }, + "targets": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["concept"], + "properties": { + "concept": { "type": "string", "pattern": "^[a-z][a-z0-9_]*$" }, + "paths": { "type": "array", "items": { "type": "string" } }, + "mask_paths": { "type": "array", "items": { "type": "string" } }, + "pairs": { + "type": "array", + "items": { + "type": "array", + "items": { "type": "string" }, + "minItems": 2, + "maxItems": 2 + } + } + } + } + }, + "tier_assignments": { + "type": "object", + "propertyNames": { "pattern": "^[a-z][a-z0-9_]*$" }, + "additionalProperties": { "enum": [1, 2, 3] } + }, + "selection_reason": { "type": "string", "minLength": 1 }, + "artifact_paths": { "type": "array", "items": { "type": "string" } }, + "full_sweep": { + "type": "object", + "additionalProperties": false, + "required": ["status"], + "properties": { + "status": { "enum": ["skipped", "approved"] }, + "reason": { "type": "string" }, + "approved_by_user": { "type": "boolean" } + } + }, + "estimated_time": { "enum": ["fast", "minutes", "long"] } + } +} diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/scripts/validator-concepts.schema.json b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/scripts/validator-concepts.schema.json new file mode 100644 index 00000000..a0d95cb1 --- /dev/null +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/scripts/validator-concepts.schema.json @@ -0,0 +1,92 @@ +{ + "$comment": "SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\nSPDX-License-Identifier: Apache-2.0", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Canonical Validator-Concept Registry", + "type": "object", + "required": ["schema_version", "concepts"], + "additionalProperties": false, + "properties": { + "schema_version": { "type": "string" }, + "$comment": { "type": "string" }, + "concepts": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/$defs/concept" } + } + }, + "$defs": { + "tier": { "enum": [1, 2, 3] }, + "concept": { + "type": "object", + "additionalProperties": false, + "required": [ + "canonical_name", + "role", + "backing_op", + "tier", + "cost_class", + "gpu_bound", + "scope_policy", + "preferred_provider", + "implementations" + ], + "properties": { + "canonical_name": { + "type": "string", + "pattern": "^[a-z][a-z0-9_]*$", + "$comment": "Lowercase snake_case only; never a runtime class name." + }, + "display_name": { "type": "string" }, + "role": { + "enum": ["safety_gate", "opportunity_detector", "target_scoping", "regression_evidence"] + }, + "backing_op": { + "type": ["string", "null"], + "$comment": "Op key the concept routes to, or null for manual/safety concepts." + }, + "tier": { "$ref": "#/$defs/tier" }, + "cost_class": { "enum": ["cheap", "medium", "expensive", "stage_dependent"] }, + "gpu_bound": { "type": "boolean" }, + "scope_policy": { "enum": ["whole_stage", "per_target_or_sample", "flagged_pairs_only"] }, + "preferred_provider": { "enum": ["so", "oav"] }, + "implementations": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/$defs/implementation" } + }, + "parameter_prerequisite": { "$ref": "#/$defs/parameter_prerequisite" }, + "notes": { "type": "string" } + } + }, + "implementation": { + "type": "object", + "additionalProperties": false, + "required": ["provider", "module", "class_name", "use_for"], + "properties": { + "provider": { "enum": ["so", "oav"] }, + "module": { "type": "string", "minLength": 1 }, + "class_name": { "type": "string", "minLength": 1 }, + "category": { + "type": "string", + "$comment": "Informational registry bucket. Resolution is by (module, class_name); category is not required." + }, + "tier": { "$ref": "#/$defs/tier", "$comment": "Per-implementation tier override (e.g. OAV slow variant)." }, + "use_for": { + "type": "array", + "minItems": 1, + "items": { "enum": ["performance_tuning", "conformance_audit", "safety_gate"] } + } + } + }, + "parameter_prerequisite": { + "type": "object", + "additionalProperties": false, + "required": ["op", "param", "question"], + "properties": { + "op": { "type": "string", "minLength": 1 }, + "param": { "type": "string", "minLength": 1 }, + "question": { "type": "string", "minLength": 1 } + } + } + } +} diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/workflow.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/workflow.md index 9e1999d8..0cc0a47f 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/workflow.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/references/workflow.md @@ -28,7 +28,9 @@ Each `references/*.md` file starts with a header block: ## The 7-phase canonical flow -Seven required phases (0-6) plus an optional Phase 7 (post-report iteration). +Seven in-flow phases (0-6) plus Phase 7. For broad "optimize this scene" +requests, Phase 7 defaults to 3 scoped iterations unless the user opts out, +asks for a quick pass, or stop criteria apply. For structured milestone lists, preserve this broad-optimization subsequence: `omniverse-usd-performance-tuning` -> `profile-stage:baseline` -> @@ -48,7 +50,7 @@ flowchart TD P4["Phase 4 Per-sub-asset mesh ops"] P5["Phase 5 Stage-level ref replacement and cleanup"] P6["Phase 6 Verify and report"] - P7["Phase 7 Optional iteration"] + P7["Phase 7 Default scoped iteration"] P0 --> P1 --> P2 P2 -->|"already_optimized"| P6 P2 -->|"exit"| P6 @@ -97,7 +99,7 @@ dispatch, the flow may continue in structural-only mode: - **Phase 3 still works** (instancing-readiness is pure USD); flips can be authored. - **Phase 4 SKIPPED** (mesh ops require SO). - **Phase 5 SKIPPED** (no optimized children to remap). -- Phase 6: `profile-stage` AFTER + `usd-validation-runner` re-validation (pre-mutation USD stack only) + `optimization-report` with verdict `structural-only` and a clear note explaining that SO operations did not run. +- Phase 6: `profile-stage` AFTER + `usd-validation-runner` re-validation (pre-mutation USD stack only) + `optimization-report` with `workflow_mode: structural_only` (the `verdict` stays in its enum — `neutral` if no metrics changed) and a `notes` entry explaining that SO operations did not run. This is the path E2E test scenarios commonly hit. @@ -138,11 +140,25 @@ Five steps (2a-2d) feeding the gate at 2e, plus optional 2f if the user chooses SA's asset_boundary_suggestions field already promotes hash-aligned cut points. -2c Phase-aware preliminary validator sweep (USE: usd-validation-runner) - Validation-runner loads `usd-validation-runner/references/validation-scoping.md` for tier - selection. Subset is driven by SA's phase_recommendation (see - Decision Tree in that reference for the full rules). - Output: a "findings corpus" that informs 2e and Phase 4 op selection. +2c Phase-aware validation scope + selected probes (USE: usd-validation-runner) + Read `usd-validation-runner/README.md` before writing or running + validator code. The runner first builds a selected validation plan from + SA's summary_counts, phase_recommendation, validation_scope, and + flagged_assets; then it runs only the selected rules/probes. + Validators are named by canonical concept (validator-concepts.json) and + executed via scripts/usd_validation_executor.py — never by bare class + name or a hand-written script. A flagged Tier 3 target's scoped probe is + mandatory (no approval); only the full-stage version is approval-gated. + Output: a compact scope note/artifact (validation-scope-note.schema.json) + plus a findings corpus that informs 2e and Phase 4 op selection. The + validation-report's coverage_ledger must be complete (every flagged + target resolved) before advancing. + + Large-stage guardrail: if resolved stage size is unknown or >100 MB, + composed prim count is >10,000, mesh/prototype count is high, the target + is customer-scale CAD/BIM/MEP/factory/plant/city, or the ask is + performance optimization rather than formal conformance, do not run a + default full-stage AV/SO sweep. Ask before full sweep. 2d Stage-level instancing assessment (USE: dedupe-candidates output from 2b) For composed stages: are existing references actually instanceable? @@ -215,6 +231,9 @@ Owner: `so-interpret-validators` (build op chain from Phase 2c findings; T3 neve geometry, non-extracted sub-hierarchies). If the assembly root has 0 mesh prims after extraction (pure Xform/reference hierarchy), skip it but log the skip decision. + Consume every `phase4_targets[]` entry; do not filter the manifest + down to prototype paths. An `assembly_root` target with retained + meshes is a mesh-optimization target, not a stage-cleanup-only target. - Composed stage: each referenced asset from Phase 2a - Monolithic-as-is: the monolith itself (N=1) @@ -232,7 +251,10 @@ Owner: `so-interpret-validators` (build op chain from Phase 2c findings; T3 neve 4c Per-target op chain (built from Phase 2c findings via so-interpret-validators): Honor prototype-first ordering: prototypes BEFORE non-prototype targets - so changes propagate. + so changes propagate. Then run the same evidence-selected mesh op chain + on every non-prototype mesh target, including an `assembly_root` target + when it retained local meshes. Stage-level cleanup comes later; it does + not replace mesh operations for geometry left in the assembly. **Internal geometry removal runs FIRST** when SA flagged containment pairs with opaque enclosures: findOccludedMeshes (analysis) → removePrims (user-confirmed deletion) @@ -243,25 +265,56 @@ Owner: `so-interpret-validators` (build op chain from Phase 2c findings; T3 neve policy before mutation. Prefer meshCleanup for vertex welding; reach for standalone mergeVertices only when the user explicitly needs that - upstream-documented behavior (see pipelines.md - "mergeVertices op vs meshCleanup.mergeVertices parameter"). - Honor ordering invariants from - so-run-operations/references/pipelines.md (merge caveats: never if - instanced/streaming). + upstream-documented behavior — the op mechanics and the + meshCleanup.mergeVertices parameter live upstream, resolved via + `references/upstreams/usd-optimize.md`. + Honor the ordering invariants in the "Operation ordering invariants" + section below (merge caveats: never if instanced/streaming). Save each optimized output to a NEW path (don't overwrite source). 4d Per-target cheap re-verify Re-run cheap validators on each optimized output to catch obvious regressions before stage assembly. Defers full re-validation to Phase 6. - After restructure/decompose, follow the "Post-Restructure Validation - Strategy" in validation-scoping.md — do not re-compose and sweep. + After restructure/decompose, follow the "Post-Restructure / + Post-Decompose Validation Strategy" in usd-validation-runner/README.md + — do not re-compose and sweep. + +4e Target completion gate (machine-checked; mirrors the validation + coverage_ledger): + Record each Phase-4 target in the optimization-report's top-level + `target_coverage.entries[]` with `path`, `role`, the default-predicate + `mesh_count`, and a `disposition` + (optimized | skipped_zero_meshes | skipped_user_declined | blocked). + Use the restructure roles (assembly_root | prototype | shared_layer | + loadable_subasset) after a restructure, and `monolith` for a + non-restructured optimize-as-is target (N=1). + `target_coverage.complete` is true only when every entry is resolved + (the first three dispositions); a `blocked` or absent target keeps it + false and the report is not final. A diagnosis-only / optimize-as-is run + with no Phase-4 work is valid with `entries: []` and `complete: true`. + The report author cannot self-attest coverage of a target that was never + enumerated, so the gate reconciles against the manifest(s). Reconciliation + is NOT optional once a restructure happened: whenever any entry has a + restructure role, record the source manifest(s) in + `target_coverage.source_manifests[]` (one per iteration). The gate + auto-loads them — and also accepts `--manifest` — and fails closed if a + restructure report has none: + python3 optimization-report/scripts/validate_report.py \ + [--manifest ] \ + [--manifest ] + The final report MUST cover the UNION of every iteration's + `phase4_targets[]` (a target listed in iter-1 but dropped from iter-2's + manifest is still owed coverage), `skipped_zero_meshes` is accepted only + when the manifest's authoritative `mesh_count` is 0, and any uncovered or + unresolved target exits non-zero. This is the gate that catches a + retained-mesh `assembly_root` left un-optimized. A `monolith`-only run + needs no manifest. Runtime branch: Kit: ops run via selected SO Python API inside Kit standalone: ops run via selected SO Python API or standalone wrapper - All Python scripts probe the selected runtime first; do not assume - `SceneOptimizerCore.getInstance()` when the runtime exposes only - `acquire_interface().execute_operation(...)`. + All Python scripts follow so-run-operations/references/invocation.md; do not + pass plain pxr.Usd.Stage objects directly to Scene Optimizer operation APIs. ``` ### Phase 4.5 - Layer cleanup after destructive in-place ops @@ -311,7 +364,7 @@ Owner: `apply-restructure` (mode=ref_remap). Same skill as Phase 2f - both phase If regressed > 5%: warn If regressed > 20%: critical, recommend revert/halt -6e optimization-report (final step of in-flow phases; honors the skill's +6d optimization-report (final step of in-flow phases; honors the skill's existing "final step" contract). Populate against the optimization-report schema (`scripts/optimization-report.schema.json` within that reference). Match optimization-report/references/optimization-report-template.md. Include baseline metrics @@ -323,7 +376,7 @@ Owner: `apply-restructure` (mode=ref_remap). Same skill as Phase 2f - both phase When returning structured plans or runtime-test milestone lists, label Phase 6a exactly `profile-stage:after`. -### Phase 7 - Iterate (optional, post-report, agent-orchestration only) +### Phase 7 - Iterate (default 3 scoped passes, post-report, agent-orchestration only) ``` 7a Compute "untapped options" - the diff between what was done and what could @@ -335,25 +388,38 @@ exactly `profile-stage:after`. - Phase 4 adaptive batching paused remaining sub-assets due to resource budget; remainder script generated -7b Offer to user (freeform, agent discretion): - "Optimization complete. Verdict: . The following were not - applied this pass: [list]. Want to run another iteration with any - of these enabled, or with adjusted parameters?" +7b Default to 3 optimization iterations for broad "optimize this scene" + requests unless the user opts out, asks for a quick pass, or the request is + diagnosis-only. Each iteration writes an interim report/update before the + next begins. -7c If user accepts iteration: - Loop back to the relevant phase (typically Phase 2c with adjusted - validator subset, or Phase 4 with new ops in the chain). Keep the - baseline metrics from the FIRST pass (don't re-baseline). +7c Iteration 1 follows the normal Phase 0-6 flow. Iterations 2 and 3 are + lighter scoped passes: reuse prior SA/profile/validation evidence, start + from the previous report's untapped options, and run only targeted/delta + probes needed to choose the next operation set. -7d If user declines: - Flow truly ends. The Phase 6e report stands as the final deliverable. +7d Loop back to the relevant phase (typically Phase 2c with adjusted selected + probes, or Phase 4 with new ops in the chain). Keep baseline metrics from + the FIRST pass (don't re-baseline). + +7e Stop before iteration 2 or 3 if no useful untapped options remain, the + previous pass regressed materially, the user opted out, or the next pass + would only repeat work. ``` -Phase 7 is optional. Always compute the "untapped options" list for transparency in the report (so the user can see what was held back even if they don't iterate). Use discretion on whether to actively offer iteration: do not nag if the user asked for "quick pass." +Phase 7 is a default three-pass posture for broad optimization, not permission +to run three full workflow reruns. Later passes are expected to be cheaper +because they reuse evidence and narrow scope. Revalidation in iterations is +same-or-narrower by default; expanded validation scope, Tier 3 cross-component +probes, full sweeps, or newly destructive operations require explicit user +approval. Always compute the "untapped options" list for transparency in the +report, even if the user opts out. ## Validator-stack matrix -The `usd-validation-runner` skill is the master router. For tier 1/2/3 detail, JSON plan template, and scene-aware adjustment rules, read `usd-validation-runner/references/validation-scoping.md`. +The `usd-validation-runner` reference is the master router. It owns tier 1/2/3 +detail, selected-probe planning, full-sweep approval, JSON plan shape, and +scene-aware adjustment rules. ### Pre-Mutation USD Stack @@ -365,15 +431,15 @@ only through their owning workflow when the user explicitly asks for them. ### Performance stack (scoped) -(read `usd-validation-runner/references/validation-scoping.md`) -> `so-run-validators` -> `so-interpret-validators`. +`usd-validation-runner` selected plan -> `so-run-validators` -> `so-interpret-validators`. ### Phase-aware subset -Owned by `usd-validation-runner/references/validation-scoping.md` → *Decision Tree*. Summary: +Owned by `usd-validation-runner/README.md`. Summary: - `structuring` → minimum-openability + targeted AV blockers only. -- `optimization` → minimum-openability + scoped AV + perf stack (Tier 1+2; Tier 3 only on flagged targets). -- `already_optimized` → minimum-openability + scoped AV + perf Tier 1 only. +- `optimization` → minimum-openability + scoped AV + perf stack (Tier 1 cheap whole-stage stats/probes + Tier 2 on flagged targets; Tier 3 scoped probes mandatory on flagged targets, full-stage Tier 3 requires approval). +- `already_optimized` → minimum-openability + scoped AV + Tier 1 cheap whole-stage stats/probes only. ## Operation ordering invariants @@ -401,6 +467,9 @@ checkout through `references/upstreams/usd-optimize.md`. - Common chain: `fitPrimitives` -> `deduplicateGeometry` -> `organizePrototypes`. - Prototype targets run before non-prototype targets; parallelize within each dependency group when resource budget allows. +- "Stage-level operations last" means an additional assembled-root cleanup + pass after per-target mesh work. It does not mean skip mesh operations for + local meshes left behind in an `assembly_root` Phase 4 target. - Bounded-loss or destructive operations run only after `operation-safety.md` confirmation. - Per-operation argument defaults and caveats come from @@ -420,16 +489,27 @@ request or as part of bespoke triage: fires and you need a breakdown before deciding between `removeSmallGeometry`, `decimateMeshes`, and `merge`. - `sparseMeshes` — exposes meshes with very low per-face vertex density; - often a sign of poor authoring or failed import. Outside the default - flow but useful when investigating draw-call counts. + often a sign of poor authoring or failed import. Treat as a Tier 2 targeted + medium probe through `usd-validation-runner`, not a cheap whole-stage default. - `utilityFunction` — meta-utility op for ad-hoc SO scripting; rarely the right tool but available when one of the recipe skills needs it. See `references/operations/utilityFunction.md`. -For lossless coincidence/occlusion analyzers (`findCoincidingGeometry`, -`findFlatHierarchies`, `findOverlappingMeshes`), -prefer running them through `so-interpret-validators` once that skill -wires them in (currently future-candidate per `_curation.json`). +The lossless coincidence/occlusion analyzers (`findCoincidingGeometry`, +`findFlatHierarchies`, `findOverlappingMeshes`) are wired as live analysis ops: +prefer running them through `so-interpret-validators`, which routes them from +validator findings. + +If you do run an analysis-only op on user request, summarize its findings as +optimization candidates, not as raw dumps: + +- `countVertices` → high-poly triage: flag the heaviest meshes as + `decimateMeshes` / `removeSmallGeometry` candidates. +- `findFlatHierarchies` → restructuring candidates: route to `flattenHierarchy` + (Xform collapse) or hierarchy dedupe. +- `findCoincidingGeometry` / `findOverlappingMeshes` → duplicate/overlap + candidates: route to `deduplicateGeometry`, `removeSmallGeometry`, or flag for + manual review. They produce a report, not a change. `findOccludedMeshes` is now wired into the Phase 4 op chain via `config-from-evidence.md` — it runs first (before all other ops) on @@ -442,18 +522,18 @@ SA-flagged containment pairs with opaque enclosures, followed by |---|---| | Phase 0: direct SO execution requested but SO unavailable | Halt with `blocked_missing_scene_optimizer`; do not substitute another workflow. | | Phase 0: requested SO op absent from the loaded runtime | Halt with `blocked_missing_so_operation`; surface supported alternatives if any. | -| Phase 0: broad optimization request, SO unavailable, and user declines install | Switch to structural-only path. Skip Phases 4-5; report `structural-only` in 6e. | +| Phase 0: broad optimization request, SO unavailable, and user declines install | Switch to structural-only path. Skip Phases 4-5; set `workflow_mode: structural_only` in the 6d report (verdict stays in its enum). | | Phase 0: User chooses "exit" at install prompt | Exit with reason "user declined runtime setup". | | Phase 1a: profile-stage fails to open the asset | Halt with diagnostic; the asset cannot be optimized if it cannot be opened. | -| Phase 2c: SA's `phase_recommendation = already_optimized` | Skip Phases 2d-5; jump to Phase 6 verify; produce report with verdict `no-op-needed`. | -| Phase 2e: User chooses "exit" at restructure gate | Skip to Phase 6e and write a diagnosis-only report. | +| Phase 2c: SA's `phase_recommendation = already_optimized` | Skip Phases 2d-5; jump to Phase 6 verify; produce report with `workflow_mode: no_op` and `verdict: neutral`. | +| Phase 2e: User chooses "exit" at restructure gate | Skip to Phase 6d and write a diagnosis-only report. | | Phase 2e: User chooses "optimize as-is" | Skip Phase 2f; continue to Phase 3 with the original stage. | | Phase 3b: All instancing candidates fail readiness | Skip Phase 3 result-application; continue to Phase 4. Note in report. | -| Phase 4d: A target's optimized output fails cheap re-verify | Discard that target's output; continue with other targets. Report failure in 6e. | +| Phase 4d: A target's optimized output fails cheap re-verify | Discard that target's output; continue with other targets. Report failure in 6d. | | Phase 6c: Verdict = regressed > 20% (critical) | Recommend revert (do not publish); user decides whether to publish anyway. | | Phase 6c: Verdict = `mixed` | Report honestly; do not present as success. | -| Phase 6e: optimization-report writes successfully | In-flow phases end. Optional Phase 7 may follow. | -| Phase 7: User declines iteration | Flow truly ends. The Phase 6e report stands as the final deliverable. | +| Phase 6d: optimization-report writes successfully | In-flow pass ends. Phase 7 may continue into the next scoped iteration unless the user opted out or stop criteria apply. | +| Phase 7: User declines iteration | Flow truly ends. The Phase 6d report stands as the final deliverable. | ## Expected duration hints (typical large stages: ~100K prims, ~200K meshes) @@ -464,9 +544,10 @@ These are guidance for setting user expectations and timeout windows, not hard S | Phase 0 | < 1 min once user choices are recorded | | Phase 1 | ~5 min (profile open + SA pass) | | Phase 2c structural validators | ~2 min | -| Phase 2c Tier 1 perf validators | ~5 min | +| Phase 2c Tier 1 cheap whole-stage stats/probes | ~5 min | | Phase 2c Tier 2 perf validators | ~30 min | -| Phase 2c Tier 3 perf validators | hours - always confirm before running | +| Phase 2c Tier 3 perf validators (scoped to flagged targets/pairs) | minutes - mandatory when flagged | +| Phase 2c Tier 3 perf validators (full-stage) | hours - always confirm before running | | Phase 4 per target | ~10-30 min depending on op chain | | Phase 5 ref-remap | ~few min for typical impact sets | | Phase 6 re-validation | same as Phase 2c | diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/skill-card.md b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/skill-card.md index 48d29ebc..4671d606 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/skill-card.md +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/skill-card.md @@ -1,14 +1,15 @@ ## Description:
-Top-level workflow skill for USD performance diagnosis and optimization. Use for slow loading, high memory, low FPS, or 'optimize my scene' requests; delegates auth/runtime setup to Phase 0 owners.
+Top-level workflow skill for USD performance diagnosis and optimization, used for slow loading, high memory, low FPS, or generic scene optimization requests.
This skill is ready for commercial/non-commercial use.
-## Owner: NVIDIA
+## Owner +NVIDIA
### License/Terms of Use:
-Apache 2.0
+Apache-2.0
## Use Case:
-Developers and engineers use this skill to diagnose and optimize USD scene performance — addressing slow loading, high memory usage, low FPS, and GPU crashes — within NVIDIA Omniverse workflows.
+Developers and engineers working with USD scenes who need to diagnose and resolve performance issues such as slow loading, high memory usage, low FPS, or GPU crashes in NVIDIA Omniverse workflows.
### Deployment Geography for Use:
Global
@@ -21,20 +22,33 @@ Mitigation: Review and scan skill before deployment.
- [Workflow Reference](references/workflow.md)
- [Skill Map](references/skill-map.md)
- [USD Structure Assessment](references/usd-structure-assessment/README.md)
-- [Scene Optimizer Operations](references/so-run-operations/README.md)
+- [Scene Optimizer Operations](references/operations/README.md)
+- [Setup USD Performance Tuning](references/setup-usd-performance-tuning/README.md)
- [USD Validation Runner](references/usd-validation-runner/README.md)
-- [Profile Stage](references/profile-stage/README.md)
-- [Compare Profiles](references/compare-profiles/README.md)
- [Optimization Report](references/optimization-report/README.md)
-- [Setup USD Performance Tuning](references/setup-usd-performance-tuning/README.md)
-- [CAD Conversion Advisor](references/cad-conversion/README.md)
+- [Scene Optimizer Run Operations](references/so-run-operations/README.md)
+- [Compare Profiles](references/compare-profiles/README.md)
+- [Profile Stage](references/profile-stage/README.md)
## Skill Output:
-**Output Type(s):** [Analysis, Shell commands, Files]
-**Output Format:** [Markdown and JSON structured reports]
+**Output Type(s):** [Analysis, Shell commands, Configuration instructions, Files]
+**Output Format:** [Markdown with structured JSON reports]
**Output Parameters:** [1D]
-**Other Properties Related to Output:** [Produces optimization-report JSON conforming to optimization-report.schema.json and rendered HTML/Markdown summaries]
+**Other Properties Related to Output:** [Produces optimization-report.schema.json-conforming reports and HTML previews via render_preview.py]
+ +## Evaluation Tasks:
+NVSkills-Eval 3-Tier evaluation (external profile): Tier 1 static validation (9 checks, 10 findings), Tier 2 deduplication analysis (2 checks, 17 findings). Tier 3 live agent evaluation not available in this report.
+ +## Evaluation Metrics Used:
+Reported benchmark dimensions:
+- Security: Checks whether skill-assisted execution avoids unsafe behavior such as secret leakage, destructive commands, or unauthorized access.
+- Correctness: Checks whether the agent follows the expected workflow and produces the correct final output.
+- Discoverability: Checks whether the agent loads the skill when relevant and avoids using it when irrelevant.
+- Effectiveness: Checks whether the agent performs measurably better with the skill than without it.
+- Efficiency: Checks whether the agent uses fewer tokens and avoids redundant work.
+ + ## Skill Version(s):
0.1.0 (source: frontmatter, pyproject.toml)
diff --git a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/skill.oms.sig b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/skill.oms.sig index 5aaecdbc..bd9e75ae 100644 --- a/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/skill.oms.sig +++ b/plugins/nvidia-skills/skills/omniverse-usd-performance-tuning/skill.oms.sig @@ -1 +1 @@ -{"mediaType":"application/vnd.dev.sigstore.bundle.v0.3+json","verificationMaterial":{"x509CertificateChain":{"certificates":[{"rawBytes":"MIICgzCCAgmgAwIBAgIUKIyS7SxNteQIiWzK1dWj85E6520wCgYIKoZIzj0EAwMwVTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjEpMCcGA1UEAwwgTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBJQ0EgMDEwHhcNMjYwNDAxMDAwMDAwWhcNMjgwNDIyMTUzMzA5WjBUMQswCQYDVQQGEwJVUzEbMBkGA1UECgwSTlZJRElBIENvcnBvcmF0aW9uMSgwJgYDVQQDDB9OVklESUEgQWdlbnQgU2tpbGxzIFNpZ25pbmcgMDAxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEYoRM9bQl/dGlwSRNi6bTpIJUXH8Nv9GciP6LSflJYYMLCc296kpyuTSsk5ddbAWiDcFX3C/ydX3jwc+qCLYP6uHy9XphyLjOQ27Yb2J6rBLVtRBS1mgGco/Gr7fL6ODco4GaMIGXMB0GA1UdDgQWBBRQ/5ZW3nJ6lmo9SVk7I15o7UGmpTAfBgNVHSMEGDAWgBRPGpILxMBBleJSsBGjrMKsby1CgjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9vY3NwLm5kaXMubnZpZGlhLmNvbTAKBggqhkjOPQQDAwNoADBlAjAUygu/GiOCIXrgGr4SmLgeEVDcEitfFUv7ALbvLVGVyMysB3mxmO/uInZfXzWcJZsCMQDxuoxj4ZmO30jhkPIcCxGFCOvnUsnfU3TfGcouYm4M6iRpbKvtVnHPiy4bi6pcKf0="},{"rawBytes":"MIICiDCCAg6gAwIBAgIUZsIuSv9NkpJCNqtYEfCouVv5BzowCgYIKoZIzj0EAwMwUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTAgFw0yNjA0MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowVTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjEpMCcGA1UEAwwgTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBJQ0EgMDEwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASI72cR3ctKGg4VWnB3bNja6g1Z2PnOmFEopkPof+QeIcPk9rT+g9MjJnq51EQXL93a7C2GJ9J985G4o2V85VD7wJ1RaXhluHW2rf3y8bQGeAYaKMr5s/hUgn+M3/9WlWejgaAwgZ0wHQYDVR0OBBYEFE8akgvEwEGV4lKwEaOswqxvLUKCMB8GA1UdIwQYMBaAFItnoAjjfuCEUvzyvWyI2vOGvwPjMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL29jc3AubmRpcy5udmlkaWEuY29tMAoGCCqGSM49BAMDA2gAMGUCMQCeIMMfAbyzPDacw2MxG+Yt1cikrJX/DVxiGfXuHmkkXn6VgSzE79+lkqDErpVO2gYCMCNEColOyvUvkzZGUEI1hQ3PfMgi3FIo9tHoBKMw4/wGBLFpu/0ubtmbBXM6/UMOEw=="},{"rawBytes":"MIICRTCCAcygAwIBAgIUeJdY3rV86EdvFmG7L8LJBsyQFYkwCgYIKoZIzj0EAwMwUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTAgFw0yNjA0MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IABAYpiXCDjJ9NT2eSDhyHJVSw1Tbze18cGG2F/578oWvHxg23eQAhNRYdq88i1iOshZSO6C29doKui5Xpmo/7Ctw9Sx4PP2RzOmIuOLCuTdNtKcTRwi4GEsd5BAFvWj42M6NjMGEwHQYDVR0OBBYEFItnoAjjfuCEUvzyvWyI2vOGvwPjMB8GA1UdIwQYMBaAFItnoAjjfuCEUvzyvWyI2vOGvwPjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2cAMGQCMCwtAjWLaNwgGWNCgdyNoTyvNhqWRECRJV2r3+7w8g0PL6NHLOsbkgE09BH95h8XlgIwTaQmbbUh2ChAJ5TA1wRiVDnCcvbzHlZl2jM2FcwQQZlk19LOAbyGMRixbu2Ww/rj"}]},"tlogEntries":[]},"dsseEnvelope":{"payload":"ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YxIiwKICAic3ViamVjdCI6IFsKICAgIHsKICAgICAgIm5hbWUiOiAib21uaXZlcnNlLXVzZC1wZXJmb3JtYW5jZS10dW5pbmciLAogICAgICAiZGlnZXN0IjogewogICAgICAgICJzaGEyNTYiOiAiNzYwNjFjZGZmYjBmMzVmMWYyOTE2MWM1YWQ0NmRmNGI3OTllYjI0ZWQ1MzliNjQ1OTkyMjlhNDRhMGE0NjZiYSIKICAgICAgfQogICAgfQogIF0sCiAgInByZWRpY2F0ZVR5cGUiOiAiaHR0cHM6Ly9tb2RlbF9zaWduaW5nL3NpZ25hdHVyZS92MS4wIiwKICAicHJlZGljYXRlIjogewogICAgInNlcmlhbGl6YXRpb24iOiB7CiAgICAgICJoYXNoX3R5cGUiOiAic2hhMjU2IiwKICAgICAgImFsbG93X3N5bWxpbmtzIjogZmFsc2UsCiAgICAgICJtZXRob2QiOiAiZmlsZXMiLAogICAgICAiaWdub3JlX3BhdGhzIjogWwogICAgICAgICIuZ2l0aHViIiwKICAgICAgICAiLmdpdGF0dHJpYnV0ZXMiLAogICAgICAgICIuZ2l0aWdub3JlIiwKICAgICAgICAiLmdpdCIKICAgICAgXQogICAgfSwKICAgICJyZXNvdXJjZXMiOiBbCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJTS0lMTC5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiYTFkOWM1ODJhZWQxMmMwMDcxMWI2YjlkMzE1MWJhMjQzOTdjYjczZDg5OWY0NzIzYWEyNDg2MDk4ZTFjNWU1YSIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogImFnZW50cy9vcGVuYWkueWFtbCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMDMxMjEzY2JiOTVmYzBkMWExZWUzNzczOTYyNzEzNjdmMjM0YzgzYWZmNjNhNWVkNWUwNTYyZjE3M2ZhODU3YiIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogImV2YWxzL2V2YWxzLmpzb24iLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImY1OWE5ZWNlZDVmMzVmNzE4ZmZkNDE4OTE2NTc3NzFmZjg2MmY0NGI3YTFiOTYzOWY2NmMxN2QwY2RhZDkzYmQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL2NhZC1jb252ZXJzaW9uL1JFQURNRS5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMzU1YTJiZTcxMjBlMTQ3Mjk1YzdiYTJlYjVkOWUzYzI3M2JhZmI0MjM3OTc2YTY3MjFjNmMzYmQyMjlhMTRiNCIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvY2FkLWNvbnZlcnNpb24vc2NyaXB0cy9jb252ZXJzaW9uLXJlcG9ydC5zY2hlbWEuanNvbiIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiODViZmY2MTYyZTY5N2U5NzkyNTFmMWJiNWJlMmY4N2I0NzE1NzkwZTllNDRlMjZhMmQ5MjllYmJkMmFiM2ZhZSIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvY29tcGFyZS1wcm9maWxlcy9SRUFETUUubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjEyZTk0MDg4OGQzYjY5YWFhMDdlZjJiZWViMTM5YzNkMzUxZDIyMzkxYjdkNzM3MWM1MTM3OWQ5OWUyYzRmMjkiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL2NvbXBhcmUtcHJvZmlsZXMubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjYwYzU3NDE5ZjQzYWVlYTM3ZWYyYzNhMWYwY2Q5ZjUzZTdlZDFiNDU0NmJkM2M0NDM0NWI4OWE0MzEzZDdkYjciCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29tbml2ZXJzZS1hdXRoZW50aWNhdGlvbi9SRUFETUUubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImVlNGRjYjMwNTM0ZGM5NGU3MDljYzI1MTI5Njc0MDkzM2ZiZDE2NzM3NDkwODNhMWQ4NGE4YmJlZWNlN2YyNzEiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvQ0xBU1NJRklDQVRJT04ubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjJmNjI1YTExMTMxODAxYjFjMDMzNjVhOTNiYmNmN2Q0YTk1Zjk3YjM2NTRhNTQ1ZjM3ZmMwMjUwYjdmZDRiZTAiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvRVhFQ1VUSU9OLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI0MDAzOTRiZWEyOWFjYzRlN2M3MjhjMDZjZTk4NGM4MjU5NTkzOTExMzA3ZmM4NmQyN2E2Y2M1ZTkwMDg4MjVhIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL1JFQURNRS5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiYTE5ZDU2ZTViNGI5ZDUxZWU2NzFlMGVkNWJkMDQ3YWM3NDFmYTdlYWVkOTg3MWMwYzA0YTlhYWFmZDVjZDJkOCIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9fY3VyYXRpb24uanNvbiIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiYjJhZTVhNzZkOTVmZWYxNWNmNjAzM2EwZDJhNDE2Y2U3ZmViYmZmMmYyYTU3ZDM0OWNlNTQ3OGNkNGNjZGIxZiIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9fdGVtcGxhdGUubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjIwOGQxMjZkYzU3OWFiZDNiNzQwYWY1YTY0ODc5NDUyOTdjOGU4NDMxZjYwMDA5OTQ3NTkwYjk2NTUyNGI1Y2EiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvYm94Q2xpcC5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiODA4MmE1OGI5MzIzN2FiOGMzM2Y5OWI1MGI4ZGY2MzY5Nzk0NDRhYWZiZWFlNmQ4Y2M3N2Q3MzkzZDZiOTYzNCIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9jb21wdXRlRXh0ZW50cy5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNTMxMzVmNGVlNzNmZTg1YzllMDFjZGY4OWQ0ZWY1YWQzODhjZWFkOWJlM2VhNmI4N2EzMTdkODdjZWFmYmQ4MyIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9jb3VudFZlcnRpY2VzLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI4NjQyMjFkM2VlNWMyMjVhNzk0MWRmZWQ2OTI1NDA3MDU3YTcwYmQwYzhhYWM2NTZkOGM1NDkxZWY4NTU2YzY2IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL2RlY2ltYXRlTWVzaGVzLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJiMTNhNmRhYWE3YjQyYWYwYTkyYjgzZTYzZTZlMzkwOGY4NjBkMjA2NWE4MmQwYWUxYjIzODQ5ODM4MDhiM2RkIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL2RlZHVwbGljYXRlR2VvbWV0cnkubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjRiZDVjNzVhOTdiOTFhYTE2ZTJlZGQ4YTA5NDUyOGRhYjVhZTk5YjUzOTBkZTJiYjI1MjI2ZjJmZDk1MzFlMWMiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvZGVkdXBsaWNhdGVIaWVyYXJjaGllcy5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNzMzN2NmN2EwOGZjYjkzZDdiZDlkNTYyYjQ4MzE2MDAwYjI1ODQwZTFiNzZiYzRjYzJkM2I2OTkyYjlhMWYxNSIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9kZWxldGVIaWRkZW5Qcmltcy5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNTNhZThiNzJmNmIxMGI3ZmE5ZDhiOTA3MGYwODgwNWZkYTY5MTk5YjI1MjRkMjRmNjczZWI2NjZmZTQxMTNhNyIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9kZWxldGVQcmltcy5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZTU1Y2NjNmE3ODgwMWNlNjdlOTk4MTBlN2FjMWI3YTdiODFjM2VlOGY1MTZhYzc2MGE5NTJiYTFkYjU4MmZiNyIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9kaWNlTWVzaGVzLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJjM2IzNThmMGIxZjA5ZjQ5MmUxYWNjMjQ1Y2EyMGM0NGQ0ZjAwM2NhYTczMjNmMTA3NWNmMjkxZDE0NWM0M2Q4IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL2VkaXRTdGFnZU1ldHJpY3MubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImE4M2Q4ZTAxNmYxMDRkNDExNjRjMDBiNmUxZDEyYmZjYmRjMTJhMDA5YzgzZTk3MTYxNTJiMDM5ZGMzOWQzOWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvZmluZENvaW5jaWRpbmdHZW9tZXRyeS5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiYjY1MWQwYzViODdkNDY1ZTVmNGVkMGI3MDllNmQ5YWJkYTkyZjcyM2M3Mjg1ZDkyZDJlYzU3MmM5MjRmMGU0OSIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9maW5kRmxhdEhpZXJhcmNoaWVzLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIzZGE0MzkyMWQ0YzliNjEyNWM4YmIxYjBhMDQ1ZTQ1YzljNWMyZmU1NDg1NmQ1MGVhYTkwNTE3ZTVlZGFlZmRmIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL2ZpbmRPY2NsdWRlZE1lc2hlcy5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiYzVmMjE1MTM4OTgzYTkzMDRjNWRiMzIyNGYzMTc3N2I5MmE4NGQ0NzY2NWNhZWFjYjk0MTIyMjg3MjMyMmFhYSIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9maW5kT3ZlcmxhcHBpbmdNZXNoZXMubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImFiNDMyNDk3MmU4YTRhNGUxM2IxMTRlOTBhMjFmMTkzNDMzNzQyMjcyNmE2ZGQwZWNhNDNlNjEwMjg2MjQ2NTkiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvZml0UHJpbWl0aXZlcy5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMWY4YjZkNmEwZTY4MGRhMDMzZDY5MWQxNjNmYzQwMzg0ZDNhMmI5Mjg2ZjU5NjM4YmM2NGU3MzU4NDZlODExYSIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9mbGF0dGVuSGllcmFyY2h5Lm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI3MzY0YWM2MDY2MWU4MDQ3NzI4ZmViN2Y2ZDE3NDhjYjlkNjQzNTI1MjU4MTE0ZWJlNDczYTdmM2M2ODZjOWU0IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL2dlbmVyYXRlQXRsYXNVVnMubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImU3YzQ4ZTBkZGQ4NDczNzc5ZjEwNTNiYzQ0OGEyYmJhZDNkZmFhNWU1YzlkMTczZTYzNjdjMzZmZWQyOTUzZGEiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvZ2VuZXJhdGVOb3JtYWxzLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJlYzljMWUwNDU5MjMxOWNlMzBmNzkzYzFmNjQ5NTFkYzQyY2QxM2E5YzZmNDYzZDNkY2QzNmY1Y2FhYTY2YzEyIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL2dlbmVyYXRlUHJvamVjdGlvblVWcy5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZDQ4OGUwNWRkMjgzYTlkNDBiOTI0OTcyNGY0ODEzYWRiNzk0NTg2MmNmMDk5NWQ3ZDk1MTI4YjU1OWQ3NDQ0ZiIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9nZW5lcmF0ZVNjZW5lLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJiOGY4MWJkODNjYTNhYmVkYTNkMDBmYmFkYzc1YzNjYTJlYTRjZDRhMGU0YTAxMjhiMDVhMmYyOTdjNjQ1OGJlIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL21hbmlmZXN0Lmpzb24iLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjRkOTQ2NGRkZDM1ZjMzYTllNjcxM2Q4MWIxMWRjYzRhOWIzODFmMmQ3OTVhNGZmZWUxNGY5NmRhMTc5ZDM3OTAiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvbWFuaWZvbGRNZXNoZXMubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjQwOTMyZTllNDg5NDJlZWExODZjM2ZmOTZiZmE2OGMxZDIyNTkxYjBkYjVhNDY3ZWJkMDdlYzRmZjQwMjg1OGYiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvbWVyZ2UubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImZmOWRiNTM5OTNhYzFjMzhkMmZlMTc1ZTRjMGIzNzFjZmRjMzkzMzZlYjc0NGMxYjNhMDBjMmQ0OGRmYzI0NWUiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvbWVyZ2VWZXJ0aWNlcy5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiYTdhY2EzM2NjODY2ZTE1NzMxYWQ2MjYwZDRjNmNmMDQwZTEyMDJkNDBlODc2ZDcxMTE5NzFmMzg2M2U3MzllNCIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9tZXNoQ2xlYW51cC5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiN2RiNWJjMGYxYmM5OTI0MWUxOTI0NTRlYjJjODM5YjViZGU5OTRjNGQ2YmY1YTJkMzgzMDBiMGVlMzNiMDkwNiIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9vcHRpbWl6ZU1hdGVyaWFscy5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNzYzOTEyMDU4ODJkNjUwOWUwYWU0ODk2M2YyZmYyMGQ2ZmYwMTllMzcxNmI2MzIwNWE0ZmRlYzE1YTE4NjFhNCIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9vcHRpbWl6ZVByaW12YXJzLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI1MmZlZmJkZmJkMzBlMzFiMjUwOTAxYjVjOTMxNjE5M2NjZjU0Zjg3NjA1MmMwMzEzOWM4ZDJjMDQwZTFhYjRiIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL29wdGltaXplU2tlbFJvb3RzLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIzZDBkNGQ1MTc0YjYwNjk3OTUyMTk0M2E2OTVlN2ZhODllZDU0YWIyNjM3MTNjMTY2ZmQzYzIyMjNhNGVlMjA0IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL29wdGltaXplVGltZVNhbXBsZXMubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImNhZjYwOGExNmQzZWRkYWIzMjUzYzJkNDZkN2NlYjFjMDM0YWI3ZjI0ZGI4MTc2MzdjYmQ2Yzc4ODM4ZTA3MjkiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvb3JnYW5pemVQcm90b3R5cGVzLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI3ZmFmNzRiMzhiYzFiOTZiYzVjOWRmNTgzZWZiNDdhNTRkMjg2ZTM4YTc2NDdmMWU3YzE3ZTMxZTc5YWM5NWM1IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL3Bpdm90Lm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJhMzU2NzcwZTdiMzgzMTgwMTQyOTM5MTE3MjFlZmNhN2NiYmNmNjQ5Y2RmNjM2OTdiNzEyMjc5MmEyY2JhYzRhIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL3ByaW1pdGl2ZXNUb01lc2hlcy5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZDY1Y2MzNGNiYmEzNDU0YjE5NThiMTEyMTAzNzhiZDRlNjljMjYyZmExOTY4MTdjNzVlNTkyYTgzZmJjNzU5NCIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9wcmludFN0YXRzLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI1ZDYxMWM0NDg0YmI0NjgxYjg2NzBhMGEyMzY5YjYyNjE2OGJjNTg4ODgzNzg1YWE2YzRlNDEyNWY4ZmQwM2FiIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL3Byb2JlLXNuYXBzaG90cy9zby0xMTAuMC40Lmpzb24iLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjkyZTNmNjQ3YjAwODk3ZjRlNTU3MzE4NTc3NWE3MjE3NjIxMmFmMjRjYzI5OTA0MTlhZTQzZjAwZGM3NDVlZGMiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvcHJ1bmVMZWF2ZXMubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImNiN2MyMDRhN2ExMmZlNTIwYWFkNjJjYWQzZDhhZTRiZDlmN2M2ZmZhMDg3YzFkNmZlYjczNzkwMzgzZDMzNWEiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvcHl0aG9uU2NyaXB0Lm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI0NzUxMGVhMWU1ZmNmMzhkZjEwMGM4YjQxNTQwOTBhNTE2OGZhYTdlZmViNjljZmU3MTllNmI5YjA3NmJiYTRjIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL3JlbWVzaE1lc2hlcy5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMmEwNmQ5MDA1NDYwYjBjNzBlMjFlNzRhYTg4NzkwNzliYTdkMWNhYTJmMzMxNmM5MjdjYWU4MDNlNmY2OGIyNyIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9yZW1vdmVBdHRyaWJ1dGVzLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJkZjcxZmI3NGQxZmFlNWIxN2NlNzZlMzYyNmY5MDhkMmMzZjBlNTA4ZTk5NDM5YmRhNjlhNDI1NjRjODgzZWNmIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL3JlbW92ZVByaW1zLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI2ZWE3OGNjOTZlYThlZjc3OGYxZTAzZTA5YTUxZWNiMGFjMmYxMGU1NDY4MjY0NTI4Mzg0YWZlZTMzNzg5ZTczIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL3JlbW92ZVNtYWxsR2VvbWV0cnkubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjY5NWFlODhmMmYwMzI0OWQ5NGExODg0NmFmZDQzMGIzZGRhZDRmMDZiOWEwMWI3MGFjYzI0NzIzZGFhZTJkNzEiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvcmVtb3ZlVW50eXBlZFByaW1zLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJjYzBhYTNhMWVjNzhjYmMyMTJjYmUzMDk5N2ZiN2QzNDc3MDVkODlkMWJiZjkzY2IzZmJiNjU0ZjY4Y2QzZjk1IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL3JlbW92ZVVudXNlZFVWcy5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiODcxODhiYzY2NjQwMjQ3ZjU3NTZhMmYxMjM2YjkwZDYyNzhjOGVkZTNhZWMyNmM0MDZkMzYwMzU0NjRjNmEyNyIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9ydHhNZXNoQ291bnQubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjk2ZDUzNGZlYWQxNTVjOTRjNzczZDUzNmMyMmIwY2ZkYTVhMmY1MzY0ZWM2Mzc0YjYyZDZhNjc2NGJjYTgxNTQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvc2NyaXB0cy9vcGVyYXRpb24tY3VyYXRpb24uc2NoZW1hLmpzb24iLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjUyNjg3Y2Q4NWIwYjAwZTc4Y2IzMDQ0NzcwMmIyMGRmODhlZWMxMDVmZDdhN2I4NzdiZWM5NzE5OTBjMGQ4YzciCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvc2NyaXB0cy9vcGVyYXRpb24tbWFuaWZlc3Quc2NoZW1hLmpzb24iLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjJjOTdiNGNlMDkyMDYzNTk1MmZiYTNiYTRmMmMzZWE5ZmJjZTJhNmRlNTkxNmNmOGJjMjE1YjNkNTU4ZmIxNDUiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvc2hyaW5rd3JhcC5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMjQwNmJlY2Q5ZjFjOTMyOWNhOWQ0YmRlZDM5ZDA2NjI4NDc3YjNkMWQ2ZGE5MzYwMDdjM2QyYTlkMjk3M2MxZiIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9zcGFyc2VNZXNoZXMubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjZiOGM1MTU3NzM2MTA4OTRhNDBiYWI0N2I3NTAyOTdhYmE5NzgxZjRkNDNmZjg2MTZhNWE2YjE5MWFjMTdiMmMiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvc3BsaXRNZXNoZXMubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjUyNWM3MGQ1MDA0ZjA1MDFhOWU1MDc5ZWY4NzBlOWMzNWQ0MjdlZGM1Njg4YWE2Njc2NWIyMDQ2MTU2YWEzOTIiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvc3ViZGl2aWRlTWVzaGVzLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIyN2JhMmY1NzUxYTA0YTNiYmNiNWUyYTAwZWJjZjcxOGYxOWUyZTAwOWFjN2ExYzI4YzYzNjRmYTVkOWMyYTUwIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL3RyaWFuZ3VsYXRlTWVzaGVzLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI1MmM4NGE4MjZkNmVlMjcyMDg1OTY3NTQ5NWZjOGUyMzBhNjM3ZmRiN2NkODBlZjk0MGRmZjIyNTc0N2EyZDkzIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL3V0aWxpdHlGdW5jdGlvbi5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZWQ0NWI1ZTA4ZGEzZWI5NmIwYjI4N2JjMjgyYTg4ZjVhZjg4YjA2NjJmNmQ2NWU0YTg2MTU3YTczOWI4MjA5YSIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3B0aW1pemF0aW9uLXJlcG9ydC9SRUFETUUubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjE5NzVhNGI5MjBlMDBjNjQyZTA3ZDg1YWM1MDc5NzIwNGJhNzc2YTVlYmZkMGI2NWRjOGZmNDI4YThkNDYyMjciCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wdGltaXphdGlvbi1yZXBvcnQvcmVmZXJlbmNlcy9vcHRpbWl6YXRpb24tcmVwb3J0LXRlbXBsYXRlLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIwZjZmMjliYzQ0ZmM1YTk1MGU4Y2RlZTdhODNlM2FiMDkwYmFmZmZiZDAzY2ZkYWVlYWFjZGI2OGI4NjJkZTkwIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcHRpbWl6YXRpb24tcmVwb3J0L3NjcmlwdHMvb3B0aW1pemF0aW9uLXJlcG9ydC5zY2hlbWEuanNvbiIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMzI1ODg3ZDYyMzE5MTQ2ZDRjZDJlNjM1YjliMDY5OTI3NGMwZThmMmM0NjRmNmJkY2VjOWZhOGEwZmI0MzBjOSIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3V0cHV0LXdvcmtzcGFjZS5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiODk5NzFmMWQxOTQ1MmYwYjM2YmFmMWIyMTFjMjZmOTQxNjQzMDQzOTFmMjZmM2NiMGRiYjIyZGZiZTI1MDM1YyIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvcHJvZmlsZS1zdGFnZS9SRUFETUUubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImVlYjllYjBjNjY4ZjJlYTc3ZTZiYjRlMTFkOTkxNzcxYTAwZGU3N2YyOTk5NDljNzVmMzM0MjA3NjEyOTVjNjciCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3JlcG9ydC10ZW1wbGF0ZXMvUkVBRE1FLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI0N2EyNjQ3MmQxMmE0NDdjNzNkMWIxMDRjMDY5ZWU1NGU0YjM0MjFlMWY0NzA3MGViYTBlNDhlNGY2NDQ1ZjIzIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9yZXBvcnQtdGVtcGxhdGVzL29wdGltaXphdGlvbi1yZXBvcnQuZGVzaWduLWZpeHR1cmUuanNvbiIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZDMzMWVlYzczNTM5OGQ0NDVkMDRkNTE3NDUyODhlYmE1NzYxNDU1OTk2MTgyYTQ0MDU3NzhmYjk0YWEyYzk2ZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvcmVwb3J0LXRlbXBsYXRlcy9vcHRpbWl6YXRpb24tcmVwb3J0Lmh0bWwudGVtcGxhdGUiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjJlNGVhZGJjMTUxM2U0NTJmMWQ0NWU1ZmNkZjJjMDJmY2Q1ZGNkNTUxMzJhZDQ5Mjc5YTVhODk5YTI1ZTdiOTUiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3JlcG9ydC10ZW1wbGF0ZXMvb3B0aW1pemF0aW9uLXJlcG9ydC5tZC50ZW1wbGF0ZSIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZTlmNDQyZDg2ZGZhY2UwNDMwZjg3NTIwZDQ1NmMxYmRiZDY5ZGQyNjg4NGFiN2QyOGE5ZDQwMTdlMDVlYzU2NiIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvcmVwb3J0LXRlbXBsYXRlcy9yZW5kZXJfcHJldmlldy5weSIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiYjEzZWZjNGIxMjBlMDllZjU4ZjIxYTU3NmI0OWUwNDMwMzkzOTBlZTc5YmUzYTgxYTdkZDg0NTExNDYwYzA5NCIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvcnVudGltZS1hcnRpZmFjdC10b2tlbi1idWRnZXQubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjQyY2E1OTM0NjFjY2JkZDljZGJlNzY4NGFmNGRlZWE1MzA1OGFjNWMxODliZTg5M2EwMzgzMzg2MDMyMGNiODgiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3NldHVwLXVzZC1wZXJmb3JtYW5jZS10dW5pbmcvUkVBRE1FLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI2YjRjZTJhMGNlMjRlZDdlOThlNzEzNGIxZjE1OTY5YzE0MDRiN2Q4ODhjZWU4MWNmMDcyOTcwM2RiMzMxZjAyIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zZXR1cC11c2QtcGVyZm9ybWFuY2UtdHVuaW5nL3JlZmVyZW5jZXMvaW5zdGFsbC1hc3NldC12YWxpZGF0b3Itc3RhbmRhbG9uZS9SRUFETUUubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjlkZDAyMGM4YTg1NWVhOWQyYmNkZjE2YWVjMTg4MzBmYTI1YWRkNGM3YTg1MWY4Y2E0YjQ3NGFhMjEyMmJlNDYiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3NldHVwLXVzZC1wZXJmb3JtYW5jZS10dW5pbmcvcmVmZXJlbmNlcy9pbnN0YWxsLWtpdC9SRUFETUUubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjM1NzQ0ODJiMmE1MDM3NmMxNGU2NGJlMzU5MzRjMzJjYWVlOTNlZjQyYWNiMzEwZmI4MjVhZjAxZmZlZTk4ZDciCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3NldHVwLXVzZC1wZXJmb3JtYW5jZS10dW5pbmcvcmVmZXJlbmNlcy9pbnN0YWxsLXNvLXN0YW5kYWxvbmUvUkVBRE1FLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJmYmFiN2Q3MTY0OTBhZmRlZDExZmNmNmYyZGMyMWQyMzA0ODQ3NTVkMjFlNjMwZDVmYzM5MTBlMjQxMWZkZjU2IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zZXR1cC11c2QtcGVyZm9ybWFuY2UtdHVuaW5nL3JlZmVyZW5jZXMvaW5zdGFsbC1zby12aWEta2l0L1JFQURNRS5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiOGJhMjViZDFmNTYyMGFkNzdhNTkxZDg4NjYzYWM0MDJhZjgwMDAwYWI3OTQ0YWJhNDljZmU4ZWE4NmQ0MGRmMyIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvc2V0dXAtdXNkLXBlcmZvcm1hbmNlLXR1bmluZy9yZWZlcmVuY2VzL2tpdC1kaXNjb3ZlcnkubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImRkYmZhYzAwN2ZkMTVmNzUyYjA2YTY5NTUyNzMwNGVjYTE4N2EwOTEzMzQzYWY4NmNlZmU2NWJiYTNmNDRhZDUiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3NldHVwLXVzZC1wZXJmb3JtYW5jZS10dW5pbmcvcmVmZXJlbmNlcy9ydW50aW1lLWNvbnRleHQtaGVhZGVyLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI5NWI0MGE0NThiZjY1MGUyMTJlYjMwMDU2MzNlMjFhZWQwOGE4N2UyYjgzMjA2ZDAzOTc1YTRkZTAzZWQ2OWM1IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zZXR1cC11c2QtcGVyZm9ybWFuY2UtdHVuaW5nL3JlZmVyZW5jZXMvcnVudGltZS1wcm9iZS5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNDFjODVmNjFiYmU1M2Y0OTQ3MjAxYjczZGQzYjZkOGIwMTRhZjNlODAxYjRkNzRhMjY2MzA5MzQyN2I1YzE2ZiIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvc2V0dXAtdXNkLXBlcmZvcm1hbmNlLXR1bmluZy9yZWZlcmVuY2VzL3N0YW5kYWxvbmUtcnVudGltZS5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMTJiZDQ2ZGQzYjYyYTJhMTQ1MGM3MGQ1MTM0OGVmMjk0NDUxYWFhYzM1OWIwYTVhZmQwOTk2Y2NkMmRkZjY2OSIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvc2V0dXAtdXNkLXBlcmZvcm1hbmNlLXR1bmluZy9zY3JpcHRzL3Byb2JlLXNuYXBzaG90LnNjaGVtYS5qc29uIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIzODNlYzIyN2NmMjc4NjI2ZGIzODIwMjkyMmIxNGZmNjY1YWE5ZDQxZGM0YWRiNjI1ZjA3NTVlY2M2OGIzMTVmIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zZXR1cC11c2QtcGVyZm9ybWFuY2UtdHVuaW5nL3NjcmlwdHMvc2V0dXAtcHJlZmxpZ2h0LnNjaGVtYS5qc29uIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIxMjA3Y2Q4YTNmMjhjN2ZkYTlmNTY4NGUzOTJhNGMxMDFmZjYwYmZiMjRmMWVjYzcxODJjMmJjNDc4NmE4M2Q1IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9za2lsbC1tYXAubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImVmN2QxNGNmMzQ4NmVlNTJmM2RmZGFiNTdmZTlkYWJjN2U2NTI5NmU2ZGZlNDk5ZTQwY2YyOTFhMmUzMmU4ZTEiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3NvLXJ1bi1vcGVyYXRpb25zL1JFQURNRS5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMzYwMzgxOWFlM2I0OGM4MGM1MGUyY2IyODBjODRhNjA2N2MyMWRkOTU3MjVlZDM1ZjFmY2ZiZjE0MTE1OTFlNiIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvc28tcnVuLW9wZXJhdGlvbnMvcmVmZXJlbmNlcy9iYXRjaC1tb2RlLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIyMmNlM2IwOTE2MmY3NjIzNTY2NzEwZGVhNGRhZmZjMzEyYjQwNzg3OTA5ZWJlYTNiZjJjOTU5YTUxYzQyNDI1IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zby1ydW4tb3BlcmF0aW9ucy9yZWZlcmVuY2VzL2NvbmZpZy1mcm9tLWV2aWRlbmNlLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJhMmI1MGZjYjNkMWExNjE4NjcwOGI0NDRjMDNlZjg5MWIzNTY3ZjI4ZWQzMWVmMmNkNjRiZWExZTRiNGM4YWUyIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zby1ydW4tb3BlcmF0aW9ucy9yZWZlcmVuY2VzL2ludm9jYXRpb24ubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjU3YWYwYzdhNWYwZmQ2N2VkYjYxZDgzMjg1MWYyOWVjZWFhYjNiNDkyMjE0MTlkMjFlNjMwZmI1ZDVhMDYwNmUiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3NvLXJ1bi1vcGVyYXRpb25zL3JlZmVyZW5jZXMvb3BlcmF0aW9uLXNhZmV0eS5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZTNkNWQwMGM0YTNlYmMxNjk3NTI3MjJiMDZlNzMxNjUyOWY5NTQ3NjBhNmMxZWQ3MGNlMzc5ZGZlOWIyMzcwNSIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvc28tcnVuLW9wZXJhdGlvbnMvcmVmZXJlbmNlcy9waXBlbGluZXMubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImY3MDQ0MjY2NmUxZjY0YzExZWMxNjRjZDYyNmJlOTJkODY1YWQ4NmUxODdhZDk0NjI4MDI3MTFmYzg3N2Y3YWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3NvLXJ1bi1vcGVyYXRpb25zL3JlZmVyZW5jZXMvc28tY3JlYXRlLXByb3h5L1JFQURNRS5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZGI0YjJkM2JhNjI3Nzk4MGMyNmY1ZWIwMTI4YTVhNmRhODRkMGMxMDdkMGQ1NDljZjkyZTNjNTQ2Y2VhOGEwYiIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvc28tcnVuLW9wZXJhdGlvbnMvcmVmZXJlbmNlcy9zby1jcmVhdGUtcHJveHkvcmVmZXJlbmNlcy9ib3VuZGluZy1ib3gtcHJveHktbW9kZXMubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImViMmQzMDRlZGZmMmJlZTcxMzFiMjJjZDBhYzc3NzM3ZTM1N2QxNTY5ZmQzYTFkOTlmMmQyZjY0YTlkY2QwZGUiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3NvLXJ1bi1vcGVyYXRpb25zL3JlZmVyZW5jZXMvc28tY3JlYXRlLXByb3h5L3JlZmVyZW5jZXMvZGVjaW1hdGUtc3RlcC1yZWNpcGVzLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI1N2VlOGRlOTQ1MzJhNzg5OTJlYjUxNzkwNjdlMjM0MTk2M2EzNmI5YjM1YzZmMWU0ZjFiOWNiMDNjZTRmNmZmIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zby1ydW4tb3BlcmF0aW9ucy9yZWZlcmVuY2VzL3NvLWNyZWF0ZS1wcm94eS9yZWZlcmVuY2VzL2RlY2ltYXRpb24tdHVuaW5nLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIzNzk3NzY4MjU1ZjkyYjU1NTA5YzAyYTAzMTc4YWFiZWE0OGZhZjdkNWY3OWIzZjBhM2Q5OTFmNDgzMDRmODIyIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zby1ydW4tb3BlcmF0aW9ucy9yZWZlcmVuY2VzL3NvLWNyZWF0ZS1wcm94eS9yZWZlcmVuY2VzL3Byb3h5LWNvbmZpZy1yZWNpcGVzLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI4NjRmYWQ2YmVjYTVlZWRiMTc3ZjQ1MGJjZGIxMGY5NjM3MDQxMzdkZTkzOTdmZjVmNmM2YzlmZmJhNjMxYzFiIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zby1ydW4tb3BlcmF0aW9ucy9yZWZlcmVuY2VzL3VuaXRzLWFuZC10b2xlcmFuY2VzLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI5NjBjNGIwODVlMWQ4NzM3MWMwNjg5ZTVjZjQ3NzJhMjgzODEzZmFmZGU0ZGVhOGM2NjMyZjkxZjVlYzUwMzc4IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91cHN0cmVhbXMvdXNkLW9wdGltaXplLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI5MWFkZGFhYjg1OTMwODk2NzQxNDkwYzMwZjc5YzJkNGFkMGU2OTUwZTQzZGNmMzBkN2QwZjAwMmNkMDQ2NjZkIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvUkVBRE1FLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJkODJhZWQzYmNlZDEwMTAwMjFkNjg5ZDNlNWUyYjA5NDg2NzhhYjg4OTk3M2NlYTRiYTgyOGZjZWYxODQ0MmI5IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvcmVmZXJlbmNlcy9hcHBseS1yZXN0cnVjdHVyZS9SRUFETUUubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImQzY2VlNTMyMDJkYzFhYWU3ZWQwNzFlODQwNDAyZmQzNjJmMzZjOWI1NzBjNDdlZmM0YzM3ZmQyZDhiNTM1NWEiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC1zdHJ1Y3R1cmUtYXNzZXNzbWVudC9yZWZlcmVuY2VzL2FwcGx5LXJlc3RydWN0dXJlL3JlZmVyZW5jZXMvaGllcmFyY2h5LWRlZHVwZS1yZXdyaXRlLXRvb2wtc3BlYy5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNzUzMDE5MGM5MDc0YzQwMjkwOGE2YzhhMzAyNzRlODg0YTU0MzdkNmM4MmJiMjc1MDhiZmM0YThkMGUwNzI1MiIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXN0cnVjdHVyZS1hc3Nlc3NtZW50L3JlZmVyZW5jZXMvYXBwbHktcmVzdHJ1Y3R1cmUvcmVmZXJlbmNlcy9yZWYtcmVtYXAtbW9kZS5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZDQyZjUwZTA1MDI0MDkyYjdjM2MzMzNkMzZjYWI5OGMxZDA1MGE3NzAxNWJjMzc0OWE4ZDE0MjUwN2M5YjBhMSIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXN0cnVjdHVyZS1hc3Nlc3NtZW50L3JlZmVyZW5jZXMvYXBwbHktcmVzdHJ1Y3R1cmUvcmVmZXJlbmNlcy9yZXN0cnVjdHVyZS1tb2RlLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJlMzc2ODY3YTIxMTI0NWVkZDliOTVlN2IwMzZkMzFlODdkOWRlNzY2YWNmODBiZWFlN2E5ZTNmNTM3ZGU5ZDc1IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvcmVmZXJlbmNlcy9hc3NldC1zdHJ1Y3R1cmUtcHJpbmNpcGxlcy5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMTIwNjFhODE2ZGIyMTdlMDZhYjhkMGVlNWIyNWFkYTk5YmYxNDFiY2NhYWUxMTZiZDNkMmM2NTRmMWQ3ZmU0NiIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXN0cnVjdHVyZS1hc3Nlc3NtZW50L3JlZmVyZW5jZXMvY29tcG9zaXRpb24tYXVkaXQubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImJjMGJjZTkxMjkzZjkxN2VkYmRmNDZkYWVmMzNmOGNmNjdmYjZkZTc4MWFlNzhiM2E1YmRiMWM4NDdmMTUxZDgiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC1zdHJ1Y3R1cmUtYXNzZXNzbWVudC9yZWZlcmVuY2VzL2ZhY3RvcnktbGV2ZWwtc3RydWN0dXJpbmcubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjI2MjFhNjlhM2ViY2VhNGVlYWY5ZGI3YzMyZWYwNGNjM2M2MjVlMGQwMDU3MDRhMjU0N2E1MjRjZDZiOTU0YTUiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC1zdHJ1Y3R1cmUtYXNzZXNzbWVudC9yZWZlcmVuY2VzL2luc3RhbmNpbmctcmVhZGluZXNzL1JFQURNRS5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNWIzOTNlZjAyMTNkZWFiZjNhNTdkZmM2MjdjZDkwMTNlNWEyOGY2YmIxMTMyOGQxMTBmYTc3MzY4Y2IxOWY2MiIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXN0cnVjdHVyZS1hc3Nlc3NtZW50L3JlZmVyZW5jZXMvaW5zdGFuY2luZy1yZWFkaW5lc3MvcmVmZXJlbmNlcy9pbnN0YW5jaW5nLWd1aWRlLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI1YzJjNWMzNWQ3ZWI5YjYyMzQxMjZjZTQzMGQwOTIwZmNmODI3MDkxNGM3OTQ5MTY5NWE3N2UzZGQzMzYwYmNkIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvcmVmZXJlbmNlcy9pbnN0YW5jaW5nLXJlYWRpbmVzcy9yZWZlcmVuY2VzL2luc3RhbmNpbmctdHJhZGVvZmZzLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIzMDdlYzM5N2FjOGExNmQ4MGFjNjkxMGIzZWQyMDIxMTg3N2ZiMzQyYjc4YjVhMWE0NGMwZjNlOWU4Mzg1Y2I3IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvcmVmZXJlbmNlcy9sYXllci1oZWFsdGgubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjgzYWM0ZWNjY2YxYjVjZDc3ZDhkZDljMDVkMWRiMjYyNTE3NDE1ZWFlNDQ5MzMwNDNjY2MzYzJmNjdlYTcxZDAiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC1zdHJ1Y3R1cmUtYXNzZXNzbWVudC9yZWZlcmVuY2VzL29wdGltaXphdGlvbi10cmFkZW9mZnMubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjNiYzlhNDQ4NmMxZGM1NzRmZjVmMzZjMmE0YmI0MmU5NTkyYjRlMjZjZTU5N2RhNDc4NTI3NjI1ODMwYmEyOWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC1zdHJ1Y3R1cmUtYXNzZXNzbWVudC9yZWZlcmVuY2VzL3Jlc3RydWN0dXJlLWRlY2lzaW9uL1JFQURNRS5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZWU0ODk3MTg2YzQwNDIxMTJlOTcxMGE2M2U2ZTcyNzMzZmRlNDU4ZjZiMjAzZTJkNWE0Y2MzZTJjYmVkMmMxMCIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXN0cnVjdHVyZS1hc3Nlc3NtZW50L3JlZmVyZW5jZXMvdXNkLWVkaXQtdGFyZ2V0LXBsYW5uZXIvUkVBRE1FLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI2NGRmMmE5NjAwOGFjYzJiODRhOTgwY2U1Y2ZkY2Q3OWY2MDJjNTIxYTBmYWVhZGYxYmMzNWZkMWFhZjhiNDRkIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvcmVmZXJlbmNlcy91c2QtZWRpdC10YXJnZXQtcGxhbm5lci9yZWZlcmVuY2VzL291dHB1dC1zYXZpbmcubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjVjMDFlM2VhYTUzNmNmMTFiMzA3MzQwOTE2YTk5ZDJiNTUzMzYzNTk0MGJiMTI0NjU3Y2Y2OGZlNmU4MmZhNmQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC1zdHJ1Y3R1cmUtYXNzZXNzbWVudC9yZWZlcmVuY2VzL3VzZC1lZGl0LXRhcmdldC1wbGFubmVyL3JlZmVyZW5jZXMvdmFyaWFudHMtcGF5bG9hZHMubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImViMjIxMWIzMjM0MThhZjM3YmU4ZWEyMDc2M2Q5YmE0YjdkZTYxNTYyZjgzOTVhYzY4M2Y5OGRlMzE4NjA1OWMiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC1zdHJ1Y3R1cmUtYXNzZXNzbWVudC9yZWZlcmVuY2VzL3VzZC1oaWVyYXJjaHktZGVkdXBlLWNhbmRpZGF0ZXMvUkVBRE1FLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIzMTg0Y2IzNzBlYjcwNDNlNjFlNDYzN2ZmNGY3MGJkODlkMmEyMjhhNjM1ZTM2YWQ4ZjNkNzI5ZmQ4NGY4NmQ1IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvcmVmZXJlbmNlcy91c2QtaGllcmFyY2h5LWRlZHVwZS1jYW5kaWRhdGVzL3JlZmVyZW5jZXMvaW5zdGFuY2UtY2FuZGlkYXRlLWZpbmRlci1zcGVjLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJmMTdmNTRhNTA4NTMzN2Q3MTU1ZWFmMjhlMDc1Yzc5YjQ4MWZmMmE0Y2Y0NGEzOTBiOGNkYzQ0YzkzOWMxNmY0IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvc2NyaXB0cy9hdWRpdC1yZXBvcnQuc2NoZW1hLmpzb24iLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImZmZjc5MDAxMTM5NDM1YTJmNWVmY2RkZTg5YjI5YzQ1NzViZWFkNTk0ZTMxNmNlY2ZkMmVkOWRiMTExOTUwMmIiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC1zdHJ1Y3R1cmUtYXNzZXNzbWVudC9zY3JpcHRzL29wdGltaXphdGlvbi1wbGFuLnNjaGVtYS5qc29uIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIxYzhmODljNTAxZjI3MTQ0YjE4YzRmNmFmM2JhMzFjZjVjNzc5YTE2Y2EyMmI5NTQ5MmU1NTI2MjRjM2FkZmM1IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvc2NyaXB0cy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQtcmVwb3J0LnNjaGVtYS5qc29uIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIwMmU0NzhiZWI2YmQyNzdhNTYyNjZkZjJhNjZiODlkNjNkMTYxZGMyZjBiNGZlZTUxOTJlYTliYmVlYzY1NTE5IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2QtdmFsaWRhdGlvbi1ydW5uZXIvUkVBRE1FLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJkYjMxNWFhYWU5MTU3ZWIwYmQ5ZGIyZThmMjIwZGZhZDg2NjNiMWJhZmZkMmY3ZTk2YTk3MDVkM2YzMzlkYjJlIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2QtdmFsaWRhdGlvbi1ydW5uZXIvcmVmZXJlbmNlcy9zby1pbnRlcnByZXQtdmFsaWRhdG9ycy9SRUFETUUubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjg2YThiYmIyMTBiMDVlYmU0MDk0OWUyY2NkN2E0NzUxMmM1MjY4YzRhM2JhNDZiYTkyZjgwMTg0YTU2OTExMTkiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC12YWxpZGF0aW9uLXJ1bm5lci9yZWZlcmVuY2VzL3NvLWludGVycHJldC12YWxpZGF0b3JzL3JlZmVyZW5jZXMvZm9sbG93LXVwLXF1ZXJpZXMubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjM3ZWRhZDQ3MjZiZWQzOWFmZjQ5N2EzMTZlZTA1NTZjZmMwYzI0ZTcwNTQ3M2MzZDVhZDU3ZmE2YTU0ODA1NDEiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC12YWxpZGF0aW9uLXJ1bm5lci9yZWZlcmVuY2VzL3NvLWludGVycHJldC12YWxpZGF0b3JzL3JlZmVyZW5jZXMvcnVsZS1yZWZlcmVuY2UubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjllNmE4NzY0MWRlZmY3ZDU0ZDgzNDEzZWY3NTNmNTViN2I2NzI2YTM4YTdkMzIxN2RiYTk2NDU1ZGNiNDFjZWYiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC12YWxpZGF0aW9uLXJ1bm5lci9yZWZlcmVuY2VzL3NvLXJ1bi12YWxpZGF0b3JzL1JFQURNRS5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNGY1MTJhZDkyNzQ1MDJiZjhkZWVjYmM1Mzk5N2E5NTc4MDlkNzg0MWMxMmMyNmUxMWE0NzRhZDg1MTEyNWRjYiIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXZhbGlkYXRpb24tcnVubmVyL3JlZmVyZW5jZXMvc28tcnVuLXZhbGlkYXRvcnMvcmVmZXJlbmNlcy9pbmZyYXN0cnVjdHVyZS5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMmU3NDQ1MzY4ODczNWVjMjM0ZmJjNWQ3YTkyOTY2MjkwM2M5NWU5MTdjOTFhOWY4ZjhkMGU0NjQ3ZjMzYjcyYiIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXZhbGlkYXRpb24tcnVubmVyL3JlZmVyZW5jZXMvdmFsaWRhdGUtdXNkLWFzc2V0LXZhbGlkYXRvci5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiYzI4YjY1YWY3NjZmOTEwZTJiMWVmNjlmMzNkYThlMGUzMjExYWUzYzFkNDU0NzE1NmM3Mjg4MWNhZTM4YmU2NyIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXZhbGlkYXRpb24tcnVubmVyL3JlZmVyZW5jZXMvdmFsaWRhdGlvbi1zY29waW5nLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJiNGI0ODFlYWVlNTg0YWQyMjIyYTcwNTNjODIxNjY0ZjZhMjY5ZjYzMThiYmU3MGJkZTBmZTE5NzA4NGQ2NDI4IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2QtdmFsaWRhdGlvbi1ydW5uZXIvc2NyaXB0cy9hc3NldC12YWxpZGF0b3ItdmFsaWRhdGlvbi1yZXBvcnQuc2NoZW1hLmpzb24iLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImZmODY0NzM2NWI3ZTExNDIwMTg5NTFkMTViOTdhNzM2MDk1OWY5OWY4ZmExYjE0YjAxYWYxZTQ0N2FlNTk1MGEiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC12YWxpZGF0aW9uLXJ1bm5lci9zY3JpcHRzL3ZhbGlkYXRpb24tcmVwb3J0LnNjaGVtYS5qc29uIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJlODFiYWQ3MzQ0MjBiYTUxYmVjOTU4YmRlN2RjNzhkYThkNzBkZDgxYTJiNTRjNjBlMGI1YThhMmIzNTk0NDAxIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy93b3JrZmxvdy5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiODc1MTI2NmE0N2E1NmYzODY5M2I4ZmE5YWI3MmRmMmEwNjI2ZTM2MWViY2UzN2JmM2ZmNzY4NWMzZGUxMzU2OSIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInNraWxsLWNhcmQubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImI3MDNlMmQzZDNjYzUzMGViNGMwYWE3MDgwYmMwZTJiN2M4NzMzZDlhNDNiYjZmY2Q1ZDdkZGE4OTI4NzBlMTEiCiAgICAgIH0KICAgIF0KICB9Cn0=","payloadType":"application/vnd.in-toto+json","signatures":[{"sig":"MGUCMGx4a/8YrTh+yy0qP2wsu7RZmN6jW9fXEQc52nfauFul/d/PDqeBjD+j43Peqhmt9AIxAPxPEo+6vSPNuvRmbTs8pcKC+V9b9TRn/XiEYZQXpNKds3FhYML2qO7cl/G9tRYKCw==","keyid":""}]}} \ No newline at end of file +{"mediaType":"application/vnd.dev.sigstore.bundle.v0.3+json","verificationMaterial":{"x509CertificateChain":{"certificates":[{"rawBytes":"MIICgzCCAgmgAwIBAgIUKIyS7SxNteQIiWzK1dWj85E6520wCgYIKoZIzj0EAwMwVTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjEpMCcGA1UEAwwgTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBJQ0EgMDEwHhcNMjYwNDAxMDAwMDAwWhcNMjgwNDIyMTUzMzA5WjBUMQswCQYDVQQGEwJVUzEbMBkGA1UECgwSTlZJRElBIENvcnBvcmF0aW9uMSgwJgYDVQQDDB9OVklESUEgQWdlbnQgU2tpbGxzIFNpZ25pbmcgMDAxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEYoRM9bQl/dGlwSRNi6bTpIJUXH8Nv9GciP6LSflJYYMLCc296kpyuTSsk5ddbAWiDcFX3C/ydX3jwc+qCLYP6uHy9XphyLjOQ27Yb2J6rBLVtRBS1mgGco/Gr7fL6ODco4GaMIGXMB0GA1UdDgQWBBRQ/5ZW3nJ6lmo9SVk7I15o7UGmpTAfBgNVHSMEGDAWgBRPGpILxMBBleJSsBGjrMKsby1CgjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9vY3NwLm5kaXMubnZpZGlhLmNvbTAKBggqhkjOPQQDAwNoADBlAjAUygu/GiOCIXrgGr4SmLgeEVDcEitfFUv7ALbvLVGVyMysB3mxmO/uInZfXzWcJZsCMQDxuoxj4ZmO30jhkPIcCxGFCOvnUsnfU3TfGcouYm4M6iRpbKvtVnHPiy4bi6pcKf0="},{"rawBytes":"MIICiDCCAg6gAwIBAgIUZsIuSv9NkpJCNqtYEfCouVv5BzowCgYIKoZIzj0EAwMwUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTAgFw0yNjA0MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowVTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjEpMCcGA1UEAwwgTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBJQ0EgMDEwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASI72cR3ctKGg4VWnB3bNja6g1Z2PnOmFEopkPof+QeIcPk9rT+g9MjJnq51EQXL93a7C2GJ9J985G4o2V85VD7wJ1RaXhluHW2rf3y8bQGeAYaKMr5s/hUgn+M3/9WlWejgaAwgZ0wHQYDVR0OBBYEFE8akgvEwEGV4lKwEaOswqxvLUKCMB8GA1UdIwQYMBaAFItnoAjjfuCEUvzyvWyI2vOGvwPjMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL29jc3AubmRpcy5udmlkaWEuY29tMAoGCCqGSM49BAMDA2gAMGUCMQCeIMMfAbyzPDacw2MxG+Yt1cikrJX/DVxiGfXuHmkkXn6VgSzE79+lkqDErpVO2gYCMCNEColOyvUvkzZGUEI1hQ3PfMgi3FIo9tHoBKMw4/wGBLFpu/0ubtmbBXM6/UMOEw=="},{"rawBytes":"MIICRTCCAcygAwIBAgIUeJdY3rV86EdvFmG7L8LJBsyQFYkwCgYIKoZIzj0EAwMwUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTAgFw0yNjA0MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IABAYpiXCDjJ9NT2eSDhyHJVSw1Tbze18cGG2F/578oWvHxg23eQAhNRYdq88i1iOshZSO6C29doKui5Xpmo/7Ctw9Sx4PP2RzOmIuOLCuTdNtKcTRwi4GEsd5BAFvWj42M6NjMGEwHQYDVR0OBBYEFItnoAjjfuCEUvzyvWyI2vOGvwPjMB8GA1UdIwQYMBaAFItnoAjjfuCEUvzyvWyI2vOGvwPjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2cAMGQCMCwtAjWLaNwgGWNCgdyNoTyvNhqWRECRJV2r3+7w8g0PL6NHLOsbkgE09BH95h8XlgIwTaQmbbUh2ChAJ5TA1wRiVDnCcvbzHlZl2jM2FcwQQZlk19LOAbyGMRixbu2Ww/rj"}]},"tlogEntries":[]},"dsseEnvelope":{"payload":"ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YxIiwKICAic3ViamVjdCI6IFsKICAgIHsKICAgICAgIm5hbWUiOiAib21uaXZlcnNlLXVzZC1wZXJmb3JtYW5jZS10dW5pbmciLAogICAgICAiZGlnZXN0IjogewogICAgICAgICJzaGEyNTYiOiAiZTgzODkyYjA2ZjFiNDJkMGMxNjE5ZDYwMzMxNjMwMmIyMWQ3ZmNmNzVmODdiYWM3MDdiOGI2OWI2YjFkNjNlYSIKICAgICAgfQogICAgfQogIF0sCiAgInByZWRpY2F0ZVR5cGUiOiAiaHR0cHM6Ly9tb2RlbF9zaWduaW5nL3NpZ25hdHVyZS92MS4wIiwKICAicHJlZGljYXRlIjogewogICAgInJlc291cmNlcyI6IFsKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImQ3N2QzMTI5YWViZDY1NTk4OTZlODUxNjEyOTdhYWQ2NDlmMWJiZmM0ZTJiODc3NGUwZTZkYzgwNWFkMTE1MmMiLAogICAgICAgICJuYW1lIjogIkJFTkNITUFSSy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjhhNzFiMDIzZWU3ZDk5YzFkNWIwNTczMTllM2I2OWY5MGUwMzg0MWMzZTliNTZkNTI1YjMzNmY5YmIyZTI2MTgiLAogICAgICAgICJuYW1lIjogIlNLSUxMLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMDMxMjEzY2JiOTVmYzBkMWExZWUzNzczOTYyNzEzNjdmMjM0YzgzYWZmNjNhNWVkNWUwNTYyZjE3M2ZhODU3YiIsCiAgICAgICAgIm5hbWUiOiAiYWdlbnRzL29wZW5haS55YW1sIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiOTg3N2E1YmM4NTVlY2YxMjNjMTVhODQ2YzFmZjIyMWYyMzM2NTJkODEwMmZkY2RjYTVhNjM4ZDI4YWEyZDRlMSIsCiAgICAgICAgIm5hbWUiOiAiZXZhbHMvZXZhbHMuanNvbiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjM1NWEyYmU3MTIwZTE0NzI5NWM3YmEyZWI1ZDllM2MyNzNiYWZiNDIzNzk3NmE2NzIxYzZjM2JkMjI5YTE0YjQiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvY2FkLWNvbnZlcnNpb24vUkVBRE1FLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiODViZmY2MTYyZTY5N2U5NzkyNTFmMWJiNWJlMmY4N2I0NzE1NzkwZTllNDRlMjZhMmQ5MjllYmJkMmFiM2ZhZSIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9jYWQtY29udmVyc2lvbi9zY3JpcHRzL2NvbnZlcnNpb24tcmVwb3J0LnNjaGVtYS5qc29uIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNDI1OTkyZTgwODBkZWEwOWUxYzRmYTQyYWVlNGJiZDcyZGQ0MTllZWMyZjc1YTAxNjg2NDIzODIxZWViOWIxOSIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9jb21wYXJlLXByb2ZpbGVzL1JFQURNRS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImExZDU4OTU4NDE2NzFmNWYwODNmNzY0MGNhMzNhNDI5NWNlM2QzZWUwNWUzNzRiZDNmYjQwZTk1YzA5ODAwNzEiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvY29tcGFyZS1wcm9maWxlcy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImVlNGRjYjMwNTM0ZGM5NGU3MDljYzI1MTI5Njc0MDkzM2ZiZDE2NzM3NDkwODNhMWQ4NGE4YmJlZWNlN2YyNzEiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb21uaXZlcnNlLWF1dGhlbnRpY2F0aW9uL1JFQURNRS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjJmNjI1YTExMTMxODAxYjFjMDMzNjVhOTNiYmNmN2Q0YTk1Zjk3YjM2NTRhNTQ1ZjM3ZmMwMjUwYjdmZDRiZTAiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9DTEFTU0lGSUNBVElPTi5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjAyZTI2YTc5NzE1MGE1ZWVkOTZjMWE3NzUyMzE1ZTQ5Yzg4MWI5M2U1ODg2MjgyOGY4NWFlZGMwMTVkMWY0ZjUiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9FWEVDVVRJT04ubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJhMTlkNTZlNWI0YjlkNTFlZTY3MWUwZWQ1YmQwNDdhYzc0MWZhN2VhZWQ5ODcxYzBjMDRhOWFhYWZkNWNkMmQ4IiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvUkVBRE1FLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNmJlNmRlYjk0MjM3NjY1NDEwMGQyMWVkZDNmMWVkNWNjMzA5YjA0OWM5N2EyOGE1MzY2ZGM1MDkwNWYwOWYwZiIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL19jdXJhdGlvbi5qc29uIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMjA4ZDEyNmRjNTc5YWJkM2I3NDBhZjVhNjQ4Nzk0NTI5N2M4ZTg0MzFmNjAwMDk5NDc1OTBiOTY1NTI0YjVjYSIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL190ZW1wbGF0ZS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjgwODJhNThiOTMyMzdhYjhjMzNmOTliNTBiOGRmNjM2OTc5NDQ0YWFmYmVhZTZkOGNjNzdkNzM5M2Q2Yjk2MzQiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9ib3hDbGlwLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNTMxMzVmNGVlNzNmZTg1YzllMDFjZGY4OWQ0ZWY1YWQzODhjZWFkOWJlM2VhNmI4N2EzMTdkODdjZWFmYmQ4MyIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL2NvbXB1dGVFeHRlbnRzLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiODY0MjIxZDNlZTVjMjI1YTc5NDFkZmVkNjkyNTQwNzA1N2E3MGJkMGM4YWFjNjU2ZDhjNTQ5MWVmODU1NmM2NiIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL2NvdW50VmVydGljZXMubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJiMTNhNmRhYWE3YjQyYWYwYTkyYjgzZTYzZTZlMzkwOGY4NjBkMjA2NWE4MmQwYWUxYjIzODQ5ODM4MDhiM2RkIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvZGVjaW1hdGVNZXNoZXMubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI0YmQ1Yzc1YTk3YjkxYWExNmUyZWRkOGEwOTQ1MjhkYWI1YWU5OWI1MzkwZGUyYmIyNTIyNmYyZmQ5NTMxZTFjIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvZGVkdXBsaWNhdGVHZW9tZXRyeS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjczMzdjZjdhMDhmY2I5M2Q3YmQ5ZDU2MmI0ODMxNjAwMGIyNTg0MGUxYjc2YmM0Y2MyZDNiNjk5MmI5YTFmMTUiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9kZWR1cGxpY2F0ZUhpZXJhcmNoaWVzLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNTNhZThiNzJmNmIxMGI3ZmE5ZDhiOTA3MGYwODgwNWZkYTY5MTk5YjI1MjRkMjRmNjczZWI2NjZmZTQxMTNhNyIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL2RlbGV0ZUhpZGRlblByaW1zLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZTU1Y2NjNmE3ODgwMWNlNjdlOTk4MTBlN2FjMWI3YTdiODFjM2VlOGY1MTZhYzc2MGE5NTJiYTFkYjU4MmZiNyIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL2RlbGV0ZVByaW1zLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiYzNiMzU4ZjBiMWYwOWY0OTJlMWFjYzI0NWNhMjBjNDRkNGYwMDNjYWE3MzIzZjEwNzVjZjI5MWQxNDVjNDNkOCIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL2RpY2VNZXNoZXMubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJhODNkOGUwMTZmMTA0ZDQxMTY0YzAwYjZlMWQxMmJmY2JkYzEyYTAwOWM4M2U5NzE2MTUyYjAzOWRjMzlkMzlkIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvZWRpdFN0YWdlTWV0cmljcy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImI2NTFkMGM1Yjg3ZDQ2NWU1ZjRlZDBiNzA5ZTZkOWFiZGE5MmY3MjNjNzI4NWQ5MmQyZWM1NzJjOTI0ZjBlNDkiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9maW5kQ29pbmNpZGluZ0dlb21ldHJ5Lm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiM2RhNDM5MjFkNGM5YjYxMjVjOGJiMWIwYTA0NWU0NWM5YzVjMmZlNTQ4NTZkNTBlYWE5MDUxN2U1ZWRhZWZkZiIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL2ZpbmRGbGF0SGllcmFyY2hpZXMubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJjNWYyMTUxMzg5ODNhOTMwNGM1ZGIzMjI0ZjMxNzc3YjkyYTg0ZDQ3NjY1Y2FlYWNiOTQxMjIyODcyMzIyYWFhIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvZmluZE9jY2x1ZGVkTWVzaGVzLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiYWI0MzI0OTcyZThhNGE0ZTEzYjExNGU5MGEyMWYxOTM0MzM3NDIyNzI2YTZkZDBlY2E0M2U2MTAyODYyNDY1OSIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL2ZpbmRPdmVybGFwcGluZ01lc2hlcy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjIwOTNmZjNkZmNmMDVlNmY2MDMzOTY1YjI3YzY0YmZmNDFjOTdiM2RhZjc4M2Q3OGFlYjNiODIyOGU5N2UxM2EiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9maXRQcmltaXRpdmVzLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNzM2NGFjNjA2NjFlODA0NzcyOGZlYjdmNmQxNzQ4Y2I5ZDY0MzUyNTI1ODExNGViZTQ3M2E3ZjNjNjg2YzllNCIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL2ZsYXR0ZW5IaWVyYXJjaHkubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJlN2M0OGUwZGRkODQ3Mzc3OWYxMDUzYmM0NDhhMmJiYWQzZGZhYTVlNWM5ZDE3M2U2MzY3YzM2ZmVkMjk1M2RhIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvZ2VuZXJhdGVBdGxhc1VWcy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImVjOWMxZTA0NTkyMzE5Y2UzMGY3OTNjMWY2NDk1MWRjNDJjZDEzYTljNmY0NjNkM2RjZDM2ZjVjYWFhNjZjMTIiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9nZW5lcmF0ZU5vcm1hbHMubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJkNDg4ZTA1ZGQyODNhOWQ0MGI5MjQ5NzI0ZjQ4MTNhZGI3OTQ1ODYyY2YwOTk1ZDdkOTUxMjhiNTU5ZDc0NDRmIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvZ2VuZXJhdGVQcm9qZWN0aW9uVVZzLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiYjhmODFiZDgzY2EzYWJlZGEzZDAwZmJhZGM3NWMzY2EyZWE0Y2Q0YTBlNGEwMTI4YjA1YTJmMjk3YzY0NThiZSIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL2dlbmVyYXRlU2NlbmUubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI0ZDk0NjRkZGQzNWYzM2E5ZTY3MTNkODFiMTFkY2M0YTliMzgxZjJkNzk1YTRmZmVlMTRmOTZkYTE3OWQzNzkwIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvbWFuaWZlc3QuanNvbiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjQwOTMyZTllNDg5NDJlZWExODZjM2ZmOTZiZmE2OGMxZDIyNTkxYjBkYjVhNDY3ZWJkMDdlYzRmZjQwMjg1OGYiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9tYW5pZm9sZE1lc2hlcy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImZmOWRiNTM5OTNhYzFjMzhkMmZlMTc1ZTRjMGIzNzFjZmRjMzkzMzZlYjc0NGMxYjNhMDBjMmQ0OGRmYzI0NWUiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9tZXJnZS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImE3YWNhMzNjYzg2NmUxNTczMWFkNjI2MGQ0YzZjZjA0MGUxMjAyZDQwZTg3NmQ3MTExOTcxZjM4NjNlNzM5ZTQiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9tZXJnZVZlcnRpY2VzLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiN2RiNWJjMGYxYmM5OTI0MWUxOTI0NTRlYjJjODM5YjViZGU5OTRjNGQ2YmY1YTJkMzgzMDBiMGVlMzNiMDkwNiIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL21lc2hDbGVhbnVwLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNzYzOTEyMDU4ODJkNjUwOWUwYWU0ODk2M2YyZmYyMGQ2ZmYwMTllMzcxNmI2MzIwNWE0ZmRlYzE1YTE4NjFhNCIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL29wdGltaXplTWF0ZXJpYWxzLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNTJmZWZiZGZiZDMwZTMxYjI1MDkwMWI1YzkzMTYxOTNjY2Y1NGY4NzYwNTJjMDMxMzljOGQyYzA0MGUxYWI0YiIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL29wdGltaXplUHJpbXZhcnMubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIzZDBkNGQ1MTc0YjYwNjk3OTUyMTk0M2E2OTVlN2ZhODllZDU0YWIyNjM3MTNjMTY2ZmQzYzIyMjNhNGVlMjA0IiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvb3B0aW1pemVTa2VsUm9vdHMubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJjYWY2MDhhMTZkM2VkZGFiMzI1M2MyZDQ2ZDdjZWIxYzAzNGFiN2YyNGRiODE3NjM3Y2JkNmM3ODgzOGUwNzI5IiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvb3B0aW1pemVUaW1lU2FtcGxlcy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjdmYWY3NGIzOGJjMWI5NmJjNWM5ZGY1ODNlZmI0N2E1NGQyODZlMzhhNzY0N2YxZTdjMTdlMzFlNzlhYzk1YzUiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9vcmdhbml6ZVByb3RvdHlwZXMubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJhMzU2NzcwZTdiMzgzMTgwMTQyOTM5MTE3MjFlZmNhN2NiYmNmNjQ5Y2RmNjM2OTdiNzEyMjc5MmEyY2JhYzRhIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvcGl2b3QubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJkNjVjYzM0Y2JiYTM0NTRiMTk1OGIxMTIxMDM3OGJkNGU2OWMyNjJmYTE5NjgxN2M3NWU1OTJhODNmYmM3NTk0IiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvcHJpbWl0aXZlc1RvTWVzaGVzLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNWQ2MTFjNDQ4NGJiNDY4MWI4NjcwYTBhMjM2OWI2MjYxNjhiYzU4ODg4Mzc4NWFhNmM0ZTQxMjVmOGZkMDNhYiIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL3ByaW50U3RhdHMubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI5MmUzZjY0N2IwMDg5N2Y0ZTU1NzMxODU3NzVhNzIxNzYyMTJhZjI0Y2MyOTkwNDE5YWU0M2YwMGRjNzQ1ZWRjIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvcHJvYmUtc25hcHNob3RzL3NvLTExMC4wLjQuanNvbiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImNiN2MyMDRhN2ExMmZlNTIwYWFkNjJjYWQzZDhhZTRiZDlmN2M2ZmZhMDg3YzFkNmZlYjczNzkwMzgzZDMzNWEiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9wcnVuZUxlYXZlcy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjQ3NTEwZWExZTVmY2YzOGRmMTAwYzhiNDE1NDA5MGE1MTY4ZmFhN2VmZWI2OWNmZTcxOWU2YjliMDc2YmJhNGMiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9weXRob25TY3JpcHQubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIyYTA2ZDkwMDU0NjBiMGM3MGUyMWU3NGFhODg3OTA3OWJhN2QxY2FhMmYzMzE2YzkyN2NhZTgwM2U2ZjY4YjI3IiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvcmVtZXNoTWVzaGVzLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZGY3MWZiNzRkMWZhZTViMTdjZTc2ZTM2MjZmOTA4ZDJjM2YwZTUwOGU5OTQzOWJkYTY5YTQyNTY0Yzg4M2VjZiIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL3JlbW92ZUF0dHJpYnV0ZXMubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI2ZWE3OGNjOTZlYThlZjc3OGYxZTAzZTA5YTUxZWNiMGFjMmYxMGU1NDY4MjY0NTI4Mzg0YWZlZTMzNzg5ZTczIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvcmVtb3ZlUHJpbXMubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI2OTVhZTg4ZjJmMDMyNDlkOTRhMTg4NDZhZmQ0MzBiM2RkYWQ0ZjA2YjlhMDFiNzBhY2MyNDcyM2RhYWUyZDcxIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvcmVtb3ZlU21hbGxHZW9tZXRyeS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImNjMGFhM2ExZWM3OGNiYzIxMmNiZTMwOTk3ZmI3ZDM0NzcwNWQ4OWQxYmJmOTNjYjNmYmI2NTRmNjhjZDNmOTUiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9yZW1vdmVVbnR5cGVkUHJpbXMubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI4NzE4OGJjNjY2NDAyNDdmNTc1NmEyZjEyMzZiOTBkNjI3OGM4ZWRlM2FlYzI2YzQwNmQzNjAzNTQ2NGM2YTI3IiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvcmVtb3ZlVW51c2VkVVZzLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiOTZkNTM0ZmVhZDE1NWM5NGM3NzNkNTM2YzIyYjBjZmRhNWEyZjUzNjRlYzYzNzRiNjJkNmE2NzY0YmNhODE1NCIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL3J0eE1lc2hDb3VudC5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjUyNjg3Y2Q4NWIwYjAwZTc4Y2IzMDQ0NzcwMmIyMGRmODhlZWMxMDVmZDdhN2I4NzdiZWM5NzE5OTBjMGQ4YzciLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9zY3JpcHRzL29wZXJhdGlvbi1jdXJhdGlvbi5zY2hlbWEuanNvbiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjJjOTdiNGNlMDkyMDYzNTk1MmZiYTNiYTRmMmMzZWE5ZmJjZTJhNmRlNTkxNmNmOGJjMjE1YjNkNTU4ZmIxNDUiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9zY3JpcHRzL29wZXJhdGlvbi1tYW5pZmVzdC5zY2hlbWEuanNvbiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjI0MDZiZWNkOWYxYzkzMjljYTlkNGJkZWQzOWQwNjYyODQ3N2IzZDFkNmRhOTM2MDA3YzNkMmE5ZDI5NzNjMWYiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9zaHJpbmt3cmFwLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNmI4YzUxNTc3MzYxMDg5NGE0MGJhYjQ3Yjc1MDI5N2FiYTk3ODFmNGQ0M2ZmODYxNmE1YTZiMTkxYWMxN2IyYyIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL3NwYXJzZU1lc2hlcy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjUyNWM3MGQ1MDA0ZjA1MDFhOWU1MDc5ZWY4NzBlOWMzNWQ0MjdlZGM1Njg4YWE2Njc2NWIyMDQ2MTU2YWEzOTIiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9zcGxpdE1lc2hlcy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjI3YmEyZjU3NTFhMDRhM2JiY2I1ZTJhMDBlYmNmNzE4ZjE5ZTJlMDA5YWM3YTFjMjhjNjM2NGZhNWQ5YzJhNTAiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9zdWJkaXZpZGVNZXNoZXMubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI1MmM4NGE4MjZkNmVlMjcyMDg1OTY3NTQ5NWZjOGUyMzBhNjM3ZmRiN2NkODBlZjk0MGRmZjIyNTc0N2EyZDkzIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvdHJpYW5ndWxhdGVNZXNoZXMubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJlZDQ1YjVlMDhkYTNlYjk2YjBiMjg3YmMyODJhODhmNWFmODhiMDY2MmY2ZDY1ZTRhODYxNTdhNzM5YjgyMDlhIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvdXRpbGl0eUZ1bmN0aW9uLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNzIxNDIwNjkwNzA3OTQ1MDQyMDM0ZWFjNjc1OGQyZmQyN2JlZDNmOGJmMjdmYzNlY2MyMWRlZjY0MTNmNzg4YSIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcHRpbWl6YXRpb24tcmVwb3J0L1JFQURNRS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjJhNGExYmQ5YjliODg5ZGJmYjYzODRmYjkyZmZjZGRjMGRjZTg5YmYxYmVlZTdkNzBiY2U4MzdmYWE4ODA4NTIiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3B0aW1pemF0aW9uLXJlcG9ydC9yZWZlcmVuY2VzL29wdGltaXphdGlvbi1yZXBvcnQtdGVtcGxhdGUubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIyYmQ5ZTQ0MzAzMDIzMDg0ODY0MGEzZmIzODVjM2MwZGEyNjZmYWMzZGYxYjA2Zjc5NGJiZWI0ZTA3MTI0YTkwIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wdGltaXphdGlvbi1yZXBvcnQvc2NyaXB0cy9vcHRpbWl6YXRpb24tcmVwb3J0LnNjaGVtYS5qc29uIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZWM5ZDdlYjBlYjQ0ZjMxNzNiZWY1MGFlZTdhY2I4YTA2MjVhYWYxNzcwOGUwMjJmYmE5ZTIwZGVlNTE2OWQxZSIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcHRpbWl6YXRpb24tcmVwb3J0L3NjcmlwdHMvdmFsaWRhdGVfcmVwb3J0LnB5IgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMWZjYmIyOWY5MGVkMGUzMjM5OWIxOTdmZTk4M2U1Zjk5OTY3ZTcwNWQ3NTVlZWVjN2Q3NjMwM2FmOGE4ZWNmYyIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vdXRwdXQtd29ya3NwYWNlLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNzMwYWE4ODYwODIyOGE4MTlkZmIxNjFiNWRkMzk0MzEwYTBmZmNhNmQ3M2NlNzVlYmU2ZDNmM2JhNzc5NTk2MCIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9wcm9maWxlLXN0YWdlL1JFQURNRS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjQ3YTI2NDcyZDEyYTQ0N2M3M2QxYjEwNGMwNjllZTU0ZTRiMzQyMWUxZjQ3MDcwZWJhMGU0OGU0ZjY0NDVmMjMiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvcmVwb3J0LXRlbXBsYXRlcy9SRUFETUUubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIzYTcyNzI1ZTViMDFiY2Y4YzNhOTY5MGI3NWIyYzdhZjcyMjZhZmIzYTg5YmM0ZWUwMjE2ODZiYTZjY2Q1NmViIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3JlcG9ydC10ZW1wbGF0ZXMvb3B0aW1pemF0aW9uLXJlcG9ydC5kZXNpZ24tZml4dHVyZS5qc29uIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNDMxMmY4YWM5OWVjZDlhODY3OGQ5NTI5MTFkMmI4NzU4NTJlMDMwNjQyMzI1MmY0N2U3ZTZlZGQwYTUyMDcwOCIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9yZXBvcnQtdGVtcGxhdGVzL29wdGltaXphdGlvbi1yZXBvcnQuZGVzaWduLWZpeHR1cmUubWFuaWZlc3QuanNvbiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjJlNGVhZGJjMTUxM2U0NTJmMWQ0NWU1ZmNkZjJjMDJmY2Q1ZGNkNTUxMzJhZDQ5Mjc5YTVhODk5YTI1ZTdiOTUiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvcmVwb3J0LXRlbXBsYXRlcy9vcHRpbWl6YXRpb24tcmVwb3J0Lmh0bWwudGVtcGxhdGUiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI3ZmEyMzI1MjUzOTQwNmViNThmZjQ4MzFhZDc1ODIzNzY5ZTY3YzZhZGQ0YjJjM2QyYmU1NDIxMTEzZTc5YmE3IiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3JlcG9ydC10ZW1wbGF0ZXMvb3B0aW1pemF0aW9uLXJlcG9ydC5tZC50ZW1wbGF0ZSIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImU5Y2MwNWY1YTFmYzY2ZTIxNDU2YTQ3MTA1NTllMDU2YWIyODNjZDZlMTgxZTRlZDk2OTBjNjM5MDgzZTE3NzIiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvcmVwb3J0LXRlbXBsYXRlcy9vcHRpbWl6YXRpb24tcmVwb3J0LnN0cnVjdHVyYWwtb25seS1maXh0dXJlLmpzb24iCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJiMTNlZmM0YjEyMGUwOWVmNThmMjFhNTc2YjQ5ZTA0MzAzOTM5MGVlNzliZTNhODFhN2RkODQ1MTE0NjBjMDk0IiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3JlcG9ydC10ZW1wbGF0ZXMvcmVuZGVyX3ByZXZpZXcucHkiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI0MmNhNTkzNDYxY2NiZGQ5Y2RiZTc2ODRhZjRkZWVhNTMwNThhYzVjMTg5YmU4OTNhMDM4MzM4NjAzMjBjYjg4IiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3J1bnRpbWUtYXJ0aWZhY3QtdG9rZW4tYnVkZ2V0Lm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiOTBkYjkxNWZlYmZlYzE0ZjNjMWZlNmM3Y2U5Y2QyNzhjNTRmN2VhZTc3YjcwMDFmMDRmYTYzMWNjM2NkMTdiZCIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zZXR1cC11c2QtcGVyZm9ybWFuY2UtdHVuaW5nL1JFQURNRS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjlkNzAxZjVjODVlMTgwNzlhNzc3MmI1N2M4ZTVkOWI0Yzk2NWNlYWZkMDQzYzU4ZjI4MDQ0YTA4MGVjYTJhYjMiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvc2V0dXAtdXNkLXBlcmZvcm1hbmNlLXR1bmluZy9yZWZlcmVuY2VzL2luc3RhbGwtYXNzZXQtdmFsaWRhdG9yLXN0YW5kYWxvbmUvUkVBRE1FLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMzU3NDQ4MmIyYTUwMzc2YzE0ZTY0YmUzNTkzNGMzMmNhZWU5M2VmNDJhY2IzMTBmYjgyNWFmMDFmZmVlOThkNyIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zZXR1cC11c2QtcGVyZm9ybWFuY2UtdHVuaW5nL3JlZmVyZW5jZXMvaW5zdGFsbC1raXQvUkVBRE1FLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNTZjZGM0Njg0ZTNkZTYwODhkNmE2MjQ4ZmFiNDIwZGMxMWRkNTY3ZmM2OWJmNGZmZmU2ODdjZDdiZTllNGUwOCIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zZXR1cC11c2QtcGVyZm9ybWFuY2UtdHVuaW5nL3JlZmVyZW5jZXMvaW5zdGFsbC1zby1zdGFuZGFsb25lL1JFQURNRS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjQyYzcwOWFkMzNlMDdkOGU2NzA3ZDczYjZmNDI0YThmNmIwZTMzYmJmMDE0NDM2NmJhMDE4NDE3MDQzNWUxMWMiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvc2V0dXAtdXNkLXBlcmZvcm1hbmNlLXR1bmluZy9yZWZlcmVuY2VzL2luc3RhbGwtc28tdmlhLWtpdC9SRUFETUUubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJmMGM4MjBiNDgzZDVmMGJhNTQzM2Y2NDNhYmI5OWY4NDI0ZTM4MGMxMGI1NzdlMjQ3ZmFhNjA1Y2QyZmUxZjIxIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3NldHVwLXVzZC1wZXJmb3JtYW5jZS10dW5pbmcvcmVmZXJlbmNlcy9raXQtZGlzY292ZXJ5Lm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMjEyMDk5NDVhMmFhNDFjZGFkZjdkZGVkNGRjOWZlOTdjZGFiZjJkZTljOGViMzI0MjYwMjNhODI0ODZlMjU5YyIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zZXR1cC11c2QtcGVyZm9ybWFuY2UtdHVuaW5nL3JlZmVyZW5jZXMvcnVudGltZS1jb250ZXh0LWhlYWRlci5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjY3NjM1MTVkZjZkOTAxN2JkYWFlNjlmMzkyZWE4ZTMzYjRjODAxZDljYjkwNzc3YjIzNDExMGUyZWEwYmNkMTAiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvc2V0dXAtdXNkLXBlcmZvcm1hbmNlLXR1bmluZy9yZWZlcmVuY2VzL3J1bnRpbWUtcHJvYmUubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIxMmJkNDZkZDNiNjJhMmExNDUwYzcwZDUxMzQ4ZWYyOTQ0NTFhYWFjMzU5YjBhNWFmZDA5OTZjY2QyZGRmNjY5IiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3NldHVwLXVzZC1wZXJmb3JtYW5jZS10dW5pbmcvcmVmZXJlbmNlcy9zdGFuZGFsb25lLXJ1bnRpbWUubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJlODkwMjVhZmQ3OWUwNzA2NGRmODRmM2MxN2U4MDg3MTA2Nzc2OTQzNDM1NTA5NDE0MGEzOTI2MGRkNjRjYTQ4IiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3NldHVwLXVzZC1wZXJmb3JtYW5jZS10dW5pbmcvc2NyaXB0cy9wcm9iZS1zbmFwc2hvdC5zY2hlbWEuanNvbiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjI1YjA1ZjYwYzUwYjhlMzhjNDczM2FiODFjZTk2MGZiMWNmZjJjNDhjMmQ2NDYzMTAyZDdmNDU1ZGM1Yjc1MWYiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvc2V0dXAtdXNkLXBlcmZvcm1hbmNlLXR1bmluZy9zY3JpcHRzL3NldHVwLXByZWZsaWdodC5zY2hlbWEuanNvbiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjA0MTQ5Mzg1OWU4OGE0ZWFjNmRjZWE4ODdiMmVlOTNiYjFlMzA1MTA5NWJlZTRiNWYyYjNkMzgyMDkwMjMyN2EiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvc2tpbGwtbWFwLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMTc0YjllNWY2MTIwZDBkOWI2MDc0NDM4MmExYjUxMTQ0OWEzODkyZmJkNTg1OTBhYzAxYWNjMWEzNDgzM2IzOSIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zby1ydW4tb3BlcmF0aW9ucy9SRUFETUUubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI0ZmViZmJiNTNiYTcwNTYzY2VmODY3YzY4NDNjYzMxOGM5NmJhZWQ0M2ZmNmI0YjVhNzgyZWNiZTdlZDA1NTU4IiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3NvLXJ1bi1vcGVyYXRpb25zL3JlZmVyZW5jZXMvYmF0Y2gtbW9kZS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImEyYjUwZmNiM2QxYTE2MTg2NzA4YjQ0NGMwM2VmODkxYjM1NjdmMjhlZDMxZWYyY2Q2NGJlYTFlNGI0YzhhZTIiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvc28tcnVuLW9wZXJhdGlvbnMvcmVmZXJlbmNlcy9jb25maWctZnJvbS1ldmlkZW5jZS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjFmNWQ1NGE4OWFjNDliMDQ3NjdmNDUzZWUzMTM2M2QzNzFiYzMwYTVlNjIxNDY3MGE3ZWJiMGFjM2FmZTg3YzMiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvc28tcnVuLW9wZXJhdGlvbnMvcmVmZXJlbmNlcy9pbnZvY2F0aW9uLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZTNkNWQwMGM0YTNlYmMxNjk3NTI3MjJiMDZlNzMxNjUyOWY5NTQ3NjBhNmMxZWQ3MGNlMzc5ZGZlOWIyMzcwNSIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zby1ydW4tb3BlcmF0aW9ucy9yZWZlcmVuY2VzL29wZXJhdGlvbi1zYWZldHkubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJmNzA0NDI2NjZlMWY2NGMxMWVjMTY0Y2Q2MjZiZTkyZDg2NWFkODZlMTg3YWQ5NDYyODAyNzExZmM4NzdmN2FkIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3NvLXJ1bi1vcGVyYXRpb25zL3JlZmVyZW5jZXMvcGlwZWxpbmVzLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZGI0YjJkM2JhNjI3Nzk4MGMyNmY1ZWIwMTI4YTVhNmRhODRkMGMxMDdkMGQ1NDljZjkyZTNjNTQ2Y2VhOGEwYiIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zby1ydW4tb3BlcmF0aW9ucy9yZWZlcmVuY2VzL3NvLWNyZWF0ZS1wcm94eS9SRUFETUUubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJlYjJkMzA0ZWRmZjJiZWU3MTMxYjIyY2QwYWM3NzczN2UzNTdkMTU2OWZkM2ExZDk5ZjJkMmY2NGE5ZGNkMGRlIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3NvLXJ1bi1vcGVyYXRpb25zL3JlZmVyZW5jZXMvc28tY3JlYXRlLXByb3h5L3JlZmVyZW5jZXMvYm91bmRpbmctYm94LXByb3h5LW1vZGVzLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNTdlZThkZTk0NTMyYTc4OTkyZWI1MTc5MDY3ZTIzNDE5NjNhMzZiOWIzNWM2ZjFlNGYxYjljYjAzY2U0ZjZmZiIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zby1ydW4tb3BlcmF0aW9ucy9yZWZlcmVuY2VzL3NvLWNyZWF0ZS1wcm94eS9yZWZlcmVuY2VzL2RlY2ltYXRlLXN0ZXAtcmVjaXBlcy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjM3OTc3NjgyNTVmOTJiNTU1MDljMDJhMDMxNzhhYWJlYTQ4ZmFmN2Q1Zjc5YjNmMGEzZDk5MWY0ODMwNGY4MjIiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvc28tcnVuLW9wZXJhdGlvbnMvcmVmZXJlbmNlcy9zby1jcmVhdGUtcHJveHkvcmVmZXJlbmNlcy9kZWNpbWF0aW9uLXR1bmluZy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjlkMzhjYmNmMmMxNWZlOGYzZDI5ZTIzMjAyNjEzM2I0ZjkwNGVjOWE3Y2M0MjkxOGIyNWE5ZTNkZmU4YTY5NzciLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvc28tcnVuLW9wZXJhdGlvbnMvcmVmZXJlbmNlcy9zby1jcmVhdGUtcHJveHkvcmVmZXJlbmNlcy9wcm94eS1jb25maWctcmVjaXBlcy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjk2MGM0YjA4NWUxZDg3MzcxYzA2ODllNWNmNDc3MmEyODM4MTNmYWZkZTRkZWE4YzY2MzJmOTFmNWVjNTAzNzgiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvc28tcnVuLW9wZXJhdGlvbnMvcmVmZXJlbmNlcy91bml0cy1hbmQtdG9sZXJhbmNlcy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjc4OWFhOGUwYWQzOTY3NjM1NzVhZTk2MTcwYThhZGY5Yjg2YzBlOWE1ZTE5YjhlYjhmNDhhMWZlMmUxNTZlMjAiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXBzdHJlYW1zL1JFQURNRS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjkxYWRkYWFiODU5MzA4OTY3NDE0OTBjMzBmNzljMmQ0YWQwZTY5NTBlNDNkY2YzMGQ3ZDBmMDAyY2QwNDY2NmQiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXBzdHJlYW1zL3VzZC1vcHRpbWl6ZS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjZmYWQ2NzBkZjI3MWIxN2UxMjlmN2U2MzVmOTQ4ZDQ1MTYxNTQ4N2Q1NGFmYTkyNDFkMzRlZDVjNWExZjQ0YjMiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXN0cnVjdHVyZS1hc3Nlc3NtZW50L1JFQURNRS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjNkYWMxMTM4ZWViOTk3Y2RlOTU1MGVlNWY5MjM3ZWY0ZTFmODhjMGIwZWQxN2YwMTY2YzhlZDhmNGJjZDVmNTMiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXN0cnVjdHVyZS1hc3Nlc3NtZW50L3JlZmVyZW5jZXMvYXBwbHktcmVzdHJ1Y3R1cmUvUkVBRE1FLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNzUzMDE5MGM5MDc0YzQwMjkwOGE2YzhhMzAyNzRlODg0YTU0MzdkNmM4MmJiMjc1MDhiZmM0YThkMGUwNzI1MiIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvcmVmZXJlbmNlcy9hcHBseS1yZXN0cnVjdHVyZS9yZWZlcmVuY2VzL2hpZXJhcmNoeS1kZWR1cGUtcmV3cml0ZS10b29sLXNwZWMubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJkNDJmNTBlMDUwMjQwOTJiN2MzYzMzM2QzNmNhYjk4YzFkMDUwYTc3MDE1YmMzNzQ5YThkMTQyNTA3YzliMGExIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC1zdHJ1Y3R1cmUtYXNzZXNzbWVudC9yZWZlcmVuY2VzL2FwcGx5LXJlc3RydWN0dXJlL3JlZmVyZW5jZXMvcmVmLXJlbWFwLW1vZGUubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIzMDBhOGE4NzVmZDZmY2Q5ZDFiNzljNDAyNjRiNzdiZGQ3YTMzYjkzZWIxMWJlZWI1Mzk3ZGFmOTM1ZGYwNjI3IiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC1zdHJ1Y3R1cmUtYXNzZXNzbWVudC9yZWZlcmVuY2VzL2FwcGx5LXJlc3RydWN0dXJlL3JlZmVyZW5jZXMvcmVzdHJ1Y3R1cmUtbW9kZS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImI5NjVjMDQxZTg3NTViNWViMGNiMjAxODA2ZTk4MGFlMmIyMzE3ZWE1MjExNWU1YmMyMGI5MTc3OGEwNDc3YzYiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXN0cnVjdHVyZS1hc3Nlc3NtZW50L3JlZmVyZW5jZXMvYXBwbHktcmVzdHJ1Y3R1cmUvc2NyaXB0cy9hcHBseS1yZXN0cnVjdHVyZS1tYW5pZmVzdC5zY2hlbWEuanNvbiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjEyMDYxYTgxNmRiMjE3ZTA2YWI4ZDBlZTViMjVhZGE5OWJmMTQxYmNjYWFlMTE2YmQzZDJjNjU0ZjFkN2ZlNDYiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXN0cnVjdHVyZS1hc3Nlc3NtZW50L3JlZmVyZW5jZXMvYXNzZXQtc3RydWN0dXJlLXByaW5jaXBsZXMubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJjN2I2YTllMDdhN2M1MjJkNTg0YWZlMzNlYzk3YTZkN2RjNmVlZTM1MzJjMjY4YThhZWZhMDU2YzBiZDMzOTNmIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC1zdHJ1Y3R1cmUtYXNzZXNzbWVudC9yZWZlcmVuY2VzL2NvbXBvc2l0aW9uLWF1ZGl0Lm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMjYyMWE2OWEzZWJjZWE0ZWVhZjlkYjdjMzJlZjA0Y2MzYzYyNWUwZDAwNTcwNGEyNTQ3YTUyNGNkNmI5NTRhNSIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvcmVmZXJlbmNlcy9mYWN0b3J5LWxldmVsLXN0cnVjdHVyaW5nLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNWIzOTNlZjAyMTNkZWFiZjNhNTdkZmM2MjdjZDkwMTNlNWEyOGY2YmIxMTMyOGQxMTBmYTc3MzY4Y2IxOWY2MiIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvcmVmZXJlbmNlcy9pbnN0YW5jaW5nLXJlYWRpbmVzcy9SRUFETUUubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI1YzJjNWMzNWQ3ZWI5YjYyMzQxMjZjZTQzMGQwOTIwZmNmODI3MDkxNGM3OTQ5MTY5NWE3N2UzZGQzMzYwYmNkIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC1zdHJ1Y3R1cmUtYXNzZXNzbWVudC9yZWZlcmVuY2VzL2luc3RhbmNpbmctcmVhZGluZXNzL3JlZmVyZW5jZXMvaW5zdGFuY2luZy1ndWlkZS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjc1NTM4NTllZTE1YzU3ZDk0OTlkZDZjZWNhMzE2NzAyZTU0MDkwM2VjMGIzM2EzYTU1MTY3YWI3ZGViMzYwMDkiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXN0cnVjdHVyZS1hc3Nlc3NtZW50L3JlZmVyZW5jZXMvaW5zdGFuY2luZy1yZWFkaW5lc3MvcmVmZXJlbmNlcy9pbnN0YW5jaW5nLXRyYWRlb2Zmcy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjgzYWM0ZWNjY2YxYjVjZDc3ZDhkZDljMDVkMWRiMjYyNTE3NDE1ZWFlNDQ5MzMwNDNjY2MzYzJmNjdlYTcxZDAiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXN0cnVjdHVyZS1hc3Nlc3NtZW50L3JlZmVyZW5jZXMvbGF5ZXItaGVhbHRoLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiM2JjOWE0NDg2YzFkYzU3NGZmNWYzNmMyYTRiYjQyZTk1OTJiNGUyNmNlNTk3ZGE0Nzg1Mjc2MjU4MzBiYTI5ZCIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvcmVmZXJlbmNlcy9vcHRpbWl6YXRpb24tdHJhZGVvZmZzLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNjEyYTZiYWIxM2EwYjI4ZDliM2ZkNTg3OWM3MzExMDY3ZjUzMjFjNzk5MTY2ZjgwNTFjYjZjZDY1ZjMyYWMzZSIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvcmVmZXJlbmNlcy9yZXN0cnVjdHVyZS1kZWNpc2lvbi9SRUFETUUubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI2NGRmMmE5NjAwOGFjYzJiODRhOTgwY2U1Y2ZkY2Q3OWY2MDJjNTIxYTBmYWVhZGYxYmMzNWZkMWFhZjhiNDRkIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC1zdHJ1Y3R1cmUtYXNzZXNzbWVudC9yZWZlcmVuY2VzL3VzZC1lZGl0LXRhcmdldC1wbGFubmVyL1JFQURNRS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjVjMDFlM2VhYTUzNmNmMTFiMzA3MzQwOTE2YTk5ZDJiNTUzMzYzNTk0MGJiMTI0NjU3Y2Y2OGZlNmU4MmZhNmQiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXN0cnVjdHVyZS1hc3Nlc3NtZW50L3JlZmVyZW5jZXMvdXNkLWVkaXQtdGFyZ2V0LXBsYW5uZXIvcmVmZXJlbmNlcy9vdXRwdXQtc2F2aW5nLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZWIyMjExYjMyMzQxOGFmMzdiZThlYTIwNzYzZDliYTRiN2RlNjE1NjJmODM5NWFjNjgzZjk4ZGUzMTg2MDU5YyIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvcmVmZXJlbmNlcy91c2QtZWRpdC10YXJnZXQtcGxhbm5lci9yZWZlcmVuY2VzL3ZhcmlhbnRzLXBheWxvYWRzLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMzE4NGNiMzcwZWI3MDQzZTYxZTQ2MzdmZjRmNzBiZDg5ZDJhMjI4YTYzNWUzNmFkOGYzZDcyOWZkODRmODZkNSIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvcmVmZXJlbmNlcy91c2QtaGllcmFyY2h5LWRlZHVwZS1jYW5kaWRhdGVzL1JFQURNRS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImYxN2Y1NGE1MDg1MzM3ZDcxNTVlYWYyOGUwNzVjNzliNDgxZmYyYTRjZjQ0YTM5MGI4Y2RjNDRjOTM5YzE2ZjQiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXN0cnVjdHVyZS1hc3Nlc3NtZW50L3JlZmVyZW5jZXMvdXNkLWhpZXJhcmNoeS1kZWR1cGUtY2FuZGlkYXRlcy9yZWZlcmVuY2VzL2luc3RhbmNlLWNhbmRpZGF0ZS1maW5kZXItc3BlYy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjI2NTFmZjQ2ZDhmYmZjZWVhOGFhMmE3OTA4OTc5NjM2ODBkZGE5MjkzOWQzZjY0ZTEwNGFjODM1ZGNkMDZkMWIiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXN0cnVjdHVyZS1hc3Nlc3NtZW50L3NjcmlwdHMvYXVkaXQtcmVwb3J0LnNjaGVtYS5qc29uIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMWM4Zjg5YzUwMWYyNzE0NGIxOGM0ZjZhZjNiYTMxY2Y1Yzc3OWExNmNhMjJiOTU0OTJlNTUyNjI0YzNhZGZjNSIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvc2NyaXB0cy9vcHRpbWl6YXRpb24tcGxhbi5zY2hlbWEuanNvbiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImQzZWJlZjQxMTE1MmRjYWFlYzFiN2VlNjQxOWMyZDQyOGFhY2FlNmI1ZTkzOTE1M2M1YTI4ZmQwYzJlMTNlNzYiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXN0cnVjdHVyZS1hc3Nlc3NtZW50L3NjcmlwdHMvdXNkLXN0cnVjdHVyZS1hc3Nlc3NtZW50LXJlcG9ydC5zY2hlbWEuanNvbiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjY4MmEzZjhlYjQ5ZTI1MTI4MDU0NTYzZjA1YjU3NTVmMWEwNTlhYmIwM2I1YjVkYmNlMmRhOWJmZmYzZmZlOTIiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXZhbGlkYXRpb24tcnVubmVyL1JFQURNRS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImJkNzBiOTRkMDY1NTQ1ZDA0ODFjZGMwZGMzYmVhZTlhMTA2YTliYmMxYzJkNTQzZWUyMjU2MTBiMzYxZGFiMjciLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXZhbGlkYXRpb24tcnVubmVyL3JlZmVyZW5jZXMvc28taW50ZXJwcmV0LXZhbGlkYXRvcnMvUkVBRE1FLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZTk0Yzc5MTIyYjY3OTZiMWExODNmMGQ2NDU1MTM0MWMyZDAwYmZkZDAxMWQ3NmJkZTNhMDc5N2RiNDIwYjVhMCIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2QtdmFsaWRhdGlvbi1ydW5uZXIvcmVmZXJlbmNlcy9zby1pbnRlcnByZXQtdmFsaWRhdG9ycy9yZWZlcmVuY2VzL2ZvbGxvdy11cC1xdWVyaWVzLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiYjMwMzRjNDJjNWVlNjRjYmZmN2I3YmI4ZjRiMzk5ZTQ1ODQ3NDVkNDEyYTc3ZGFlMWY0OGIyMDg1MjhlNTdmMCIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2QtdmFsaWRhdGlvbi1ydW5uZXIvcmVmZXJlbmNlcy9zby1pbnRlcnByZXQtdmFsaWRhdG9ycy9yZWZlcmVuY2VzL3J1bGUtcmVmZXJlbmNlLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiM2IzM2YwYTY4YTE3ZDgyNjk5NGQ3YjczZDc5NzgxODA5MzRlYTc1NmQyYWNjZjc3NzBiZTFiNmNlOWU4MmVjNiIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2QtdmFsaWRhdGlvbi1ydW5uZXIvcmVmZXJlbmNlcy9zby1ydW4tdmFsaWRhdG9ycy9SRUFETUUubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJmNjE0NDIyMTU2YjdiMDQ0Njc1YmM4YzUwZTBmNDNiMTY3NDE3NmRjMzA2Mzg5YTZmMGZmODc2YzE0MjY5YmI2IiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC12YWxpZGF0aW9uLXJ1bm5lci9yZWZlcmVuY2VzL3NvLXJ1bi12YWxpZGF0b3JzL3JlZmVyZW5jZXMvaW5mcmFzdHJ1Y3R1cmUubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJlNTNlNjg1NzY1NTYwMGRjNGFjMTg1MjE5M2E2MmVmYmYzMzkxMDNmNmRjNWRhNTZmNmY5ZWE3OWViNDRmYmVjIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC12YWxpZGF0aW9uLXJ1bm5lci9yZWZlcmVuY2VzL3ZhbGlkYXRlLXVzZC1hc3NldC12YWxpZGF0b3IubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJmODhhNGQzZWRhMjk5ZDg2NzAzMDgwMGY3NGIxZDgwNmZmMmI4ZDJkMDM1NTNiNzJiYTIzMTYwNWQzY2ViMDViIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC12YWxpZGF0aW9uLXJ1bm5lci9yZWZlcmVuY2VzL3ZhbGlkYXRvci1jb25jZXB0cy5qc29uIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMmZkYWE0M2YyODczZGU2MTM2NTQwZGI5ZTUwNmM3ZWIyNmI0ZWYwNWZkMDY5NmY5YjBkMjFmNzJkNzY2MzMxMCIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2QtdmFsaWRhdGlvbi1ydW5uZXIvc2NyaXB0cy91c2RfdmFsaWRhdGlvbl9leGVjdXRvci5weSIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjljOTlhZjI3OWUyZDMwZjg3MGZmMjk1ZGQ3N2ZkYzBiNjA1NGFmMzAxMmEyZDI3ZWRkOGZkZTYyZjA0M2E0MDgiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXZhbGlkYXRpb24tcnVubmVyL3NjcmlwdHMvdmFsaWRhdGlvbi1yZXBvcnQuc2NoZW1hLmpzb24iCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJmNWNiNGFlN2Q5ZDc5NWYzYjgwYWFiMmJkMWNiYjZjMzc3NzNiYzJmMTVkZTYzNmFmMTczYmQxOGI0NGFjYTAxIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC12YWxpZGF0aW9uLXJ1bm5lci9zY3JpcHRzL3ZhbGlkYXRpb24tc2NvcGUtbm90ZS5zY2hlbWEuanNvbiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImNmNTI1ZWQ2MzBmZmMzYTAyYzgwOTBiZDQ3NTVkNGQzNDBmYTEyN2JlMDMyNWIyYmY3OTkwMGE2YTc4MWRjYTciLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXZhbGlkYXRpb24tcnVubmVyL3NjcmlwdHMvdmFsaWRhdG9yLWNvbmNlcHRzLnNjaGVtYS5qc29uIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZTcyOTYxM2JhYzRjZmRkZmYyY2Y3Njc2NDAxM2Y2ZGYwOGQyOGY2ZjJhYWVjMzM5OTU1NTRkNTg1MTllNTc1ZSIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy93b3JrZmxvdy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjc3NWVmOTMyMmQzMzBiMmRlZjAzZTNjNzRjOGRjMzNjZmRhNDExY2Q5NDk0YmEzYmY4NDUxOGQ4MWQ3NWU0ODQiLAogICAgICAgICJuYW1lIjogInNraWxsLWNhcmQubWQiCiAgICAgIH0KICAgIF0sCiAgICAic2VyaWFsaXphdGlvbiI6IHsKICAgICAgImlnbm9yZV9wYXRocyI6IFsKICAgICAgICAiLmdpdGh1YiIsCiAgICAgICAgIi5naXRpZ25vcmUiLAogICAgICAgICIuZ2l0IiwKICAgICAgICAiLmdpdGF0dHJpYnV0ZXMiCiAgICAgIF0sCiAgICAgICJtZXRob2QiOiAiZmlsZXMiLAogICAgICAiaGFzaF90eXBlIjogInNoYTI1NiIsCiAgICAgICJhbGxvd19zeW1saW5rcyI6IGZhbHNlCiAgICB9CiAgfQp9","payloadType":"application/vnd.in-toto+json","signatures":[{"sig":"MGUCMQD4bjufbPKiPuqBBuobdiE76lbwM7nBOvqJa0ikxsXnFYD98+sHlSiXRPDETQwP0AsCMDJuG2+rsHKufVj2CZr6vrS/BpfyUCsOoPn8ua++wEykO1y1YtYqGge8hKCH2MwedA==","keyid":""}]}} \ No newline at end of file diff --git a/skills/omniverse-usd-performance-tuning/BENCHMARK.md b/skills/omniverse-usd-performance-tuning/BENCHMARK.md index 3774721d..1778a9e1 100644 --- a/skills/omniverse-usd-performance-tuning/BENCHMARK.md +++ b/skills/omniverse-usd-performance-tuning/BENCHMARK.md @@ -40,7 +40,7 @@ Tier 3 dimension rollup was not available in this report. ## Tier 1: Static Validation Summary -Tier 1 validation passed with observations. NVSkills-Eval ran 9 checks and found 12 total findings. +Tier 1 validation passed with observations. NVSkills-Eval ran 9 checks and found 10 total findings. Top findings: @@ -52,28 +52,11 @@ Top findings: ## Tier 2: Deduplication Summary -Tier 2 validation reported findings. NVSkills-Eval ran 2 checks and found 18 total findings. +Tier 2 validation reported findings. NVSkills-Eval ran 2 checks and found 17 total findings. Top findings: -- HIGH DUPLICATE/duplicate: Duplicate content found across references/usd-structure-assessment/references/apply-restructure/references/ref-remap-mode.md and references/usd-structure-assessment/references/apply-restructure/references/restructure-mode.md: - "## Output Validation" in references/usd-structure-assessment/references/apply-restructure/references/ref-remap-mode.md (lines 73-76) - vs "## Output Validation" in references/usd-structure-assessment/references/apply-restructure/references/restructure-mode.md (lines 178-182) (`references/usd-structure-assessment/references/apply-restructure/references/ref-remap-mode.md:73`) -- HIGH DUPLICATE/duplicate: Duplicate content found across references/compare-profiles/README.md and references/omniverse-authentication/README.md and references/optimization-report/README.md and references/profile-stage/README.md and references/setup-usd-performance-tuning/references/install-kit/README.md and references/setup-usd-performance-tuning/references/install-so-standalone/README.md and references/setup-usd-performance-tuning/references/install-so-via-kit/README.md and references/usd-structure-assessment/README.md and references/usd-structure-assessment/references/apply-restructure/README.md and references/usd-structure-assessment/references/instancing-readiness/README.md and references/usd-structure-assessment/references/restructure-decision/README.md and references/usd-structure-assessment/references/usd-edit-target-planner/README.md and references/usd-structure-assessment/references/usd-hierarchy-dedupe-candidates/README.md: - "## Instructions" in references/compare-profiles/README.md (lines 10-17) - vs "## Instructions" in references/omniverse-authentication/README.md (lines 10-16) - vs "## Instructions" in references/optimization-report/README.md (lines 10-21) - vs "## Instructions" in references/profile-stage/README.md (lines 10-17) - vs "## Instructions" in references/setup-usd-performance-tuning/references/install-kit/README.md (lines 10-16) - vs "## Instructions" in references/setup-usd-performance-tuning/references/install-so-standalone/README.md (lines 10-16) - vs "## Instructions" in references/setup-usd-performance-tuning/references/install-so-via-kit/README.md (lines 10-16) - vs "## Instructions" in references/usd-structure-assessment/README.md (lines 10-16) - vs "## Instructions" in references/usd-structure-assessment/references/apply-restructure/README.md (lines 10-16) - vs "## Instructions" in references/usd-structure-assessment/references/instancing-readiness/README.md (lines 10-16) - vs "## Instructions" in references/usd-structure-assessment/references/restructure-decision/README.md (lines 10-17) - vs "## Instructions" in references/usd-structure-assessment/references/usd-edit-target-planner/README.md (lines 10-16) - vs "## Instructions" in references/usd-structure-assessment/references/usd-hierarchy-dedupe-candidates/README.md (lines 10-16) (`references/compare-profiles/README.md:10`) -- HIGH DUPLICATE/duplicate: Duplicate content found across references/cad-conversion/README.md and references/compare-profiles.md and references/operations/CLASSIFICATION.md and references/operations/EXECUTION.md and references/operations/_template.md and references/operations/boxClip.md and references/operations/computeExtents.md and references/operations/countVertices.md and references/operations/decimateMeshes.md and references/operations/deduplicateGeometry.md and references/operations/deduplicateHierarchies.md and references/operations/deleteHiddenPrims.md and references/operations/deletePrims.md and references/operations/diceMeshes.md and references/operations/editStageMetrics.md and references/operations/findCoincidingGeometry.md and references/operations/findFlatHierarchies.md and references/operations/findOccludedMeshes.md and references/operations/findOverlappingMeshes.md and references/operations/fitPrimitives.md and references/operations/flattenHierarchy.md and references/operations/generateAtlasUVs.md and references/operations/generateNormals.md and references/operations/generateProjectionUVs.md and references/operations/generateScene.md and references/operations/manifoldMeshes.md and references/operations/merge.md and references/operations/mergeVertices.md and references/operations/meshCleanup.md and references/operations/optimizeMaterials.md and references/operations/optimizePrimvars.md and references/operations/optimizeSkelRoots.md and references/operations/optimizeTimeSamples.md and references/operations/organizePrototypes.md and references/operations/pivot.md and references/operations/primitivesToMeshes.md and references/operations/printStats.md and references/operations/pruneLeaves.md and references/operations/pythonScript.md and references/operations/remeshMeshes.md and references/operations/removeAttributes.md and references/operations/removePrims.md and references/operations/removeSmallGeometry.md and references/operations/removeUntypedPrims.md and references/operations/removeUnusedUVs.md and references/operations/rtxMeshCount.md and references/operations/shrinkwrap.md and references/operations/sparseMeshes.md and references/operations/splitMeshes.md and references/operations/subdivideMeshes.md and references/operations/triangulateMeshes.md and references/operations/utilityFunction.md and references/optimization-report/references/optimization-report-template.md and references/output-workspace.md and references/report-templates/README.md and references/runtime-artifact-token-budget.md and references/setup-usd-performance-tuning/references/kit-discovery.md and references/setup-usd-performance-tuning/references/runtime-context-header.md and references/setup-usd-performance-tuning/references/runtime-probe.md and references/setup-usd-performance-tuning/references/standalone-runtime.md and references/skill-map.md and references/so-run-operations/README.md and references/so-run-operations/references/batch-mode.md and references/so-run-operations/references/config-from-evidence.md and references/so-run-operations/references/invocation.md and references/so-run-operations/references/operation-safety.md and references/so-run-operations/references/pipelines.md and references/so-run-operations/references/so-create-proxy/README.md and references/so-run-operations/references/so-create-proxy/references/bounding-box-proxy-modes.md and references/so-run-operations/references/so-create-proxy/references/decimate-step-recipes.md and references/so-run-operations/references/so-create-proxy/references/decimation-tuning.md and references/so-run-operations/references/so-create-proxy/references/proxy-config-recipes.md and references/so-run-operations/references/units-and-tolerances.md and references/upstreams/usd-optimize.md and references/usd-structure-assessment/references/apply-restructure/references/hierarchy-dedupe-rewrite-tool-spec.md and references/usd-structure-assessment/references/apply-restructure/references/ref-remap-mode.md and references/usd-structure-assessment/references/apply-restructure/references/restructure-mode.md and references/usd-structure-assessment/references/asset-structure-principles.md and references/usd-structure-assessment/references/composition-audit.md and references/usd-structure-assessment/references/factory-level-structuring.md and references/usd-structure-assessment/references/instancing-readiness/references/instancing-guide.md and references/usd-structure-assessment/references/instancing-readiness/references/instancing-tradeoffs.md and references/usd-structure-assessment/references/layer-health.md and references/usd-structure-assessment/references/optimization-tradeoffs.md and references/usd-structure-assessment/references/usd-edit-target-planner/references/output-saving.md and references/usd-structure-assessment/references/usd-edit-target-planner/references/variants-payloads.md and references/usd-structure-assessment/references/usd-hierarchy-dedupe-candidates/references/instance-candidate-finder-spec.md and references/usd-validation-runner/references/so-interpret-validators/README.md and references/usd-validation-runner/references/so-interpret-validators/references/follow-up-queries.md and references/usd-validation-runner/references/so-interpret-validators/references/rule-reference.md and references/usd-validation-runner/references/so-run-validators/README.md and references/usd-validation-runner/references/so-run-validators/references/infrastructure.md and references/usd-validation-runner/references/validate-usd-asset-validator.md and references/usd-validation-runner/references/validation-scoping.md and references/workflow.md: +- HIGH DUPLICATE/duplicate: Duplicate content found across references/cad-conversion/README.md and references/compare-profiles.md and references/operations/CLASSIFICATION.md and references/operations/EXECUTION.md and references/operations/_template.md and references/operations/boxClip.md and references/operations/computeExtents.md and references/operations/countVertices.md and references/operations/decimateMeshes.md and references/operations/deduplicateGeometry.md and references/operations/deduplicateHierarchies.md and references/operations/deleteHiddenPrims.md and references/operations/deletePrims.md and references/operations/diceMeshes.md and references/operations/editStageMetrics.md and references/operations/findCoincidingGeometry.md and references/operations/findFlatHierarchies.md and references/operations/findOccludedMeshes.md and references/operations/findOverlappingMeshes.md and references/operations/fitPrimitives.md and references/operations/flattenHierarchy.md and references/operations/generateAtlasUVs.md and references/operations/generateNormals.md and references/operations/generateProjectionUVs.md and references/operations/generateScene.md and references/operations/manifoldMeshes.md and references/operations/merge.md and references/operations/mergeVertices.md and references/operations/meshCleanup.md and references/operations/optimizeMaterials.md and references/operations/optimizePrimvars.md and references/operations/optimizeSkelRoots.md and references/operations/optimizeTimeSamples.md and references/operations/organizePrototypes.md and references/operations/pivot.md and references/operations/primitivesToMeshes.md and references/operations/printStats.md and references/operations/pruneLeaves.md and references/operations/pythonScript.md and references/operations/remeshMeshes.md and references/operations/removeAttributes.md and references/operations/removePrims.md and references/operations/removeSmallGeometry.md and references/operations/removeUntypedPrims.md and references/operations/removeUnusedUVs.md and references/operations/rtxMeshCount.md and references/operations/shrinkwrap.md and references/operations/sparseMeshes.md and references/operations/splitMeshes.md and references/operations/subdivideMeshes.md and references/operations/triangulateMeshes.md and references/operations/utilityFunction.md and references/optimization-report/references/optimization-report-template.md and references/output-workspace.md and references/report-templates/README.md and references/runtime-artifact-token-budget.md and references/setup-usd-performance-tuning/references/kit-discovery.md and references/setup-usd-performance-tuning/references/runtime-context-header.md and references/setup-usd-performance-tuning/references/runtime-probe.md and references/setup-usd-performance-tuning/references/standalone-runtime.md and references/skill-map.md and references/so-run-operations/README.md and references/so-run-operations/references/batch-mode.md and references/so-run-operations/references/config-from-evidence.md and references/so-run-operations/references/invocation.md and references/so-run-operations/references/operation-safety.md and references/so-run-operations/references/pipelines.md and references/so-run-operations/references/so-create-proxy/README.md and references/so-run-operations/references/so-create-proxy/references/bounding-box-proxy-modes.md and references/so-run-operations/references/so-create-proxy/references/decimate-step-recipes.md and references/so-run-operations/references/so-create-proxy/references/decimation-tuning.md and references/so-run-operations/references/so-create-proxy/references/proxy-config-recipes.md and references/so-run-operations/references/units-and-tolerances.md and references/upstreams/usd-optimize.md and references/usd-structure-assessment/references/apply-restructure/references/hierarchy-dedupe-rewrite-tool-spec.md and references/usd-structure-assessment/references/apply-restructure/references/ref-remap-mode.md and references/usd-structure-assessment/references/apply-restructure/references/restructure-mode.md and references/usd-structure-assessment/references/asset-structure-principles.md and references/usd-structure-assessment/references/composition-audit.md and references/usd-structure-assessment/references/factory-level-structuring.md and references/usd-structure-assessment/references/instancing-readiness/references/instancing-guide.md and references/usd-structure-assessment/references/instancing-readiness/references/instancing-tradeoffs.md and references/usd-structure-assessment/references/layer-health.md and references/usd-structure-assessment/references/optimization-tradeoffs.md and references/usd-structure-assessment/references/usd-edit-target-planner/references/output-saving.md and references/usd-structure-assessment/references/usd-edit-target-planner/references/variants-payloads.md and references/usd-structure-assessment/references/usd-hierarchy-dedupe-candidates/references/instance-candidate-finder-spec.md and references/usd-validation-runner/references/so-interpret-validators/README.md and references/usd-validation-runner/references/so-interpret-validators/references/follow-up-queries.md and references/usd-validation-runner/references/so-interpret-validators/references/rule-reference.md and references/usd-validation-runner/references/so-run-validators/README.md and references/usd-validation-runner/references/so-run-validators/references/infrastructure.md and references/usd-validation-runner/references/validate-usd-asset-validator.md and references/workflow.md: "(preamble)" in references/cad-conversion/README.md (lines 1-3) vs "(preamble)" in references/compare-profiles.md (lines 1-3) vs "(preamble)" in references/operations/CLASSIFICATION.md (lines 1-3) @@ -167,12 +150,26 @@ Top findings: vs "(preamble)" in references/usd-validation-runner/references/so-run-validators/README.md (lines 1-3) vs "(preamble)" in references/usd-validation-runner/references/so-run-validators/references/infrastructure.md (lines 1-3) vs "(preamble)" in references/usd-validation-runner/references/validate-usd-asset-validator.md (lines 1-3) - vs "(preamble)" in references/usd-validation-runner/references/validation-scoping.md (lines 1-3) vs "(preamble)" in references/workflow.md (lines 1-3) (`references/cad-conversion/README.md:1`) -- HIGH DUPLICATE/duplicate: Duplicate content found across SKILL.md and references/compare-profiles/README.md and references/omniverse-authentication/README.md and references/profile-stage/README.md and references/setup-usd-performance-tuning/references/install-kit/README.md and references/setup-usd-performance-tuning/references/install-so-standalone/README.md and references/setup-usd-performance-tuning/references/install-so-via-kit/README.md and references/usd-structure-assessment/README.md and references/usd-structure-assessment/references/apply-restructure/README.md and references/usd-structure-assessment/references/instancing-readiness/README.md and references/usd-structure-assessment/references/restructure-decision/README.md and references/usd-structure-assessment/references/usd-edit-target-planner/README.md and references/usd-structure-assessment/references/usd-hierarchy-dedupe-candidates/README.md and references/usd-validation-runner/README.md and references/usd-validation-runner/references/so-run-validators/README.md: - "## Instructions" in SKILL.md (lines 10-19) - vs "## Output Format" in SKILL.md (lines 20-26) - vs "## Output Format" in references/compare-profiles/README.md (lines 26-33) +- HIGH DUPLICATE/duplicate: Duplicate content found across references/usd-structure-assessment/references/apply-restructure/references/ref-remap-mode.md and references/usd-structure-assessment/references/apply-restructure/references/restructure-mode.md: + "## Output Validation" in references/usd-structure-assessment/references/apply-restructure/references/ref-remap-mode.md (lines 73-76) + vs "## Output Validation" in references/usd-structure-assessment/references/apply-restructure/references/restructure-mode.md (lines 179-183) (`references/usd-structure-assessment/references/apply-restructure/references/ref-remap-mode.md:73`) +- HIGH DUPLICATE/duplicate: Duplicate content found across references/compare-profiles/README.md and references/omniverse-authentication/README.md and references/optimization-report/README.md and references/profile-stage/README.md and references/setup-usd-performance-tuning/references/install-kit/README.md and references/setup-usd-performance-tuning/references/install-so-standalone/README.md and references/setup-usd-performance-tuning/references/install-so-via-kit/README.md and references/usd-structure-assessment/README.md and references/usd-structure-assessment/references/apply-restructure/README.md and references/usd-structure-assessment/references/instancing-readiness/README.md and references/usd-structure-assessment/references/restructure-decision/README.md and references/usd-structure-assessment/references/usd-edit-target-planner/README.md and references/usd-structure-assessment/references/usd-hierarchy-dedupe-candidates/README.md: + "## Instructions" in references/compare-profiles/README.md (lines 10-17) + vs "## Instructions" in references/omniverse-authentication/README.md (lines 10-16) + vs "## Instructions" in references/optimization-report/README.md (lines 10-21) + vs "## Instructions" in references/profile-stage/README.md (lines 10-17) + vs "## Instructions" in references/setup-usd-performance-tuning/references/install-kit/README.md (lines 10-16) + vs "## Instructions" in references/setup-usd-performance-tuning/references/install-so-standalone/README.md (lines 10-16) + vs "## Instructions" in references/setup-usd-performance-tuning/references/install-so-via-kit/README.md (lines 10-16) + vs "## Instructions" in references/usd-structure-assessment/README.md (lines 10-16) + vs "## Instructions" in references/usd-structure-assessment/references/apply-restructure/README.md (lines 10-16) + vs "## Instructions" in references/usd-structure-assessment/references/instancing-readiness/README.md (lines 10-16) + vs "## Instructions" in references/usd-structure-assessment/references/restructure-decision/README.md (lines 10-17) + vs "## Instructions" in references/usd-structure-assessment/references/usd-edit-target-planner/README.md (lines 10-16) + vs "## Instructions" in references/usd-structure-assessment/references/usd-hierarchy-dedupe-candidates/README.md (lines 10-16) (`references/compare-profiles/README.md:10`) +- HIGH DUPLICATE/duplicate: Duplicate content found across references/compare-profiles/README.md and references/omniverse-authentication/README.md and references/profile-stage/README.md and references/setup-usd-performance-tuning/references/install-kit/README.md and references/setup-usd-performance-tuning/references/install-so-standalone/README.md and references/setup-usd-performance-tuning/references/install-so-via-kit/README.md and references/usd-structure-assessment/README.md and references/usd-structure-assessment/references/apply-restructure/README.md and references/usd-structure-assessment/references/instancing-readiness/README.md and references/usd-structure-assessment/references/restructure-decision/README.md and references/usd-structure-assessment/references/usd-edit-target-planner/README.md and references/usd-structure-assessment/references/usd-hierarchy-dedupe-candidates/README.md and references/usd-validation-runner/references/so-run-validators/README.md: + "## Output Format" in references/compare-profiles/README.md (lines 26-33) vs "## Output Format" in references/omniverse-authentication/README.md (lines 17-20) vs "## Output Format" in references/profile-stage/README.md (lines 28-34) vs "## Output Format" in references/setup-usd-performance-tuning/references/install-kit/README.md (lines 17-20) @@ -184,8 +181,7 @@ Top findings: vs "## Output Format" in references/usd-structure-assessment/references/restructure-decision/README.md (lines 27-30) vs "## Output Format" in references/usd-structure-assessment/references/usd-edit-target-planner/README.md (lines 17-20) vs "## Output Format" in references/usd-structure-assessment/references/usd-hierarchy-dedupe-candidates/README.md (lines 17-24) - vs "## Output Format" in references/usd-validation-runner/README.md (lines 28-31) - vs "## Output Format" in references/usd-validation-runner/references/so-run-validators/README.md (lines 29-33) (`SKILL.md:10`) + vs "## Output Format" in references/usd-validation-runner/references/so-run-validators/README.md (lines 29-33) (`references/compare-profiles/README.md:26`) - HIGH DUPLICATE/duplicate: Duplicate content found across references/usd-structure-assessment/references/asset-structure-principles.md and references/usd-structure-assessment/references/instancing-readiness/references/instancing-guide.md: "#### Asset Parameterization" in references/usd-structure-assessment/references/asset-structure-principles.md (lines 259-286) vs "### By parameterization" in references/usd-structure-assessment/references/instancing-readiness/references/instancing-guide.md (lines 153-188) (`references/usd-structure-assessment/references/asset-structure-principles.md:259`) diff --git a/skills/omniverse-usd-performance-tuning/SKILL.md b/skills/omniverse-usd-performance-tuning/SKILL.md index a7b3c6d7..e7fbee88 100644 --- a/skills/omniverse-usd-performance-tuning/SKILL.md +++ b/skills/omniverse-usd-performance-tuning/SKILL.md @@ -33,7 +33,7 @@ Use this workflow for broad performance asks such as slow loading, high memory, 1. Start from the mandatory runtime context gate before producing tuning output, unless the prompt is only asking for a static classification test. 2. Classify broad optimization requests as `ready_to_plan`; reserve `approval_required` for prompts that explicitly name a destructive operation to execute before planning. -3. Plan the full canonical chain through `optimization-report`, preserving the structured milestone order and the `profile-stage:baseline` / `profile-stage:after` labels when listing milestones. +3. Plan the full canonical chain through `optimization-report`, preserving the structured milestone order and the `profile-stage:baseline` / `profile-stage:after` labels when listing milestones. For broad optimization, default to 3 scoped iterations unless the user opts out, asks for a quick pass, or stop criteria apply. 4. Invoke downstream skill bodies only when their phase is reached, and keep raw runtime artifacts on disk while reading compact summaries. Frontmatter keeps `version` and `tools` at top level for agentskills.io runtime @@ -41,7 +41,7 @@ compatibility. NVCARPS discoverability fields live under `metadata`. ## Output Format -Return a plan or status summary that names the selected entry skill, uses `ready_to_plan` for generic optimization requests, includes the full milestone chain through `optimization-report`, and labels profile phases as `profile-stage:baseline` and `profile-stage:after`. For structured outputs, the broad-optimization milestone subsequence is `omniverse-usd-performance-tuning` -> `profile-stage:baseline` -> `usd-structure-assessment` -> `usd-validation-runner` -> `restructure-decision` -> `apply-restructure` -> `so-run-validators` -> `so-interpret-validators` -> `so-run-operations` -> `profile-stage:after` -> `compare-profiles` -> `optimization-report`. End-to-end execution should produce an optimized stage when mutation runs and a report conforming to the `optimization-report` reference's schema (`scripts/optimization-report.schema.json` within that reference). +Return a plan or status summary that names the selected entry skill, uses `ready_to_plan` for generic optimization requests, includes the full milestone chain through `optimization-report`, and labels profile phases as `profile-stage:baseline` and `profile-stage:after`. For structured outputs, the broad-optimization milestone subsequence is `omniverse-usd-performance-tuning` -> `profile-stage:baseline` -> `usd-structure-assessment` -> `usd-validation-runner` -> `restructure-decision` -> `apply-restructure` -> `so-run-validators` -> `so-interpret-validators` -> `so-run-operations` -> `profile-stage:after` -> `compare-profiles` -> `optimization-report`. End-to-end execution should produce an optimized stage when mutation runs and a report conforming to the `optimization-report` reference's schema (`scripts/optimization-report.schema.json` within that reference). Broad optimization should plan 3 scoped iterations by default; each iteration writes an interim report/update and later passes reuse prior evidence instead of restarting the full workflow. Use this workflow for broad performance asks such as slow loading, low FPS, high memory, GPU crashes, conversion quality, or "optimize my scene." @@ -248,7 +248,8 @@ If you have network access, prefer the live URLs (noted in each reference file) Read `references/workflow.md` for the canonical Phase 0-7 flow, including Kit/standalone branches, validator-stack routing, operation ordering, -termination conditions, duration hints, and the optional iteration pattern. +termination conditions, duration hints, and the default three-pass scoped +iteration pattern. The compact root map at `references/skill-map.md` only routes agents into this workflow. @@ -268,7 +269,7 @@ For deeper subtopic guidance, consult the references: - `references/cad-conversion/README.md` - CAD converter settings. - `references/upstreams/usd-optimize.md` - upstream SO mechanics and prebuilt package resolution. - `skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-run-validators/references/infrastructure.md` - local handoff for SO validator infrastructure. -- `skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/validation-scoping.md` - tier 1/2/3 plan + scene-aware adjustment. +- `skills/omniverse-usd-performance-tuning/references/usd-validation-runner/README.md` - tier 1/2/3 selected-probe plan, large-stage guardrails, full-sweep approval, and scene-aware adjustment. - `skills/omniverse-usd-performance-tuning/references/optimization-report/references/optimization-report-template.md` - the data contract every phase populates. For full Kit runtime profiling (FPS, frame time, Hydra/RTX metrics), refer to the external profiling skills at NVIDIA/omniperf. diff --git a/skills/omniverse-usd-performance-tuning/evals/evals.json b/skills/omniverse-usd-performance-tuning/evals/evals.json index 59189090..a6b24b40 100644 --- a/skills/omniverse-usd-performance-tuning/evals/evals.json +++ b/skills/omniverse-usd-performance-tuning/evals/evals.json @@ -69,6 +69,60 @@ "Does not run profiling, validation, Scene Optimizer operations, or optimization-report steps.", "Routes to a viewer or application-building skill if one is available." ] + }, + { + "id": "usd-performance-structural-only-report", + "question": "Optimize /tmp/factory.usd, but Scene Optimizer is not installed in my runtime and I don't want to install anything right now. Still give me a report.", + "expected_skill": "omniverse-usd-performance-tuning", + "expected_script": null, + "ground_truth": "With Scene Optimizer unavailable and install declined, the workflow runs in structural-only mode: structure assessment plus pre-mutation USD validation, no mesh operations. The final report keeps verdict within its enum (neutral if nothing changed), sets workflow_mode to structural_only, and records the Scene Optimizer blocker in notes. It must not invent a structural-only verdict value.", + "expected_behavior": [ + "Reads the omniverse-usd-performance-tuning SKILL.md.", + "Recognizes Scene Optimizer is unavailable and install was declined, and continues in structural-only mode rather than halting or fabricating results.", + "Skips the Scene Optimizer mesh-operation phases.", + "Produces a report whose verdict stays within improved|neutral|regressed|mixed and sets workflow_mode to structural_only.", + "Records the Scene Optimizer blocker and the next profile capture needed in the report notes." + ] + }, + { + "id": "usd-performance-report-schema-conformance", + "question": "Finish the optimization run on /tmp/factory.usd and write the final report.", + "expected_skill": "omniverse-usd-performance-tuning", + "expected_script": null, + "ground_truth": "The final optimization report must conform to optimization-report.schema.json. The agent should validate the report JSON with python3 scripts/validate_report.py before treating it as final, generate HTML via the renderer with --fixture/--output, and must not emit out-of-enum verdicts or invented top-level fields.", + "expected_behavior": [ + "Reads the omniverse-usd-performance-tuning SKILL.md and the optimization-report reference.", + "Generates the report JSON against optimization-report.schema.json with verdict in improved|neutral|regressed|mixed.", + "Validates the finished report with python3 scripts/validate_report.py before finishing.", + "Generates the HTML via render_preview.py with --fixture and --output, not by hand and not argless.", + "Does not invent verdict values such as structural-only or no-op-needed." + ] + }, + { + "id": "usd-performance-no-overwrite-source", + "question": "Run mesh cleanup and decimation on /data/asset.usd to make it lighter.", + "expected_skill": "omniverse-usd-performance-tuning", + "expected_script": null, + "ground_truth": "Destructive Scene Optimizer operations must write a new optimized output path and must not overwrite the source asset in place. The agent should also ask for approval before the explicitly named destructive operations.", + "expected_behavior": [ + "Reads the omniverse-usd-performance-tuning SKILL.md.", + "Asks for approval before the explicitly named destructive operations (mesh cleanup, decimation).", + "Saves optimized output to a new path and does not overwrite the original source asset.", + "Validates before and after the operations and records the output path in the optimization report." + ] + }, + { + "id": "usd-performance-zero-work-operation", + "question": "I ran the optimization and the operation report says it completed - did anything actually change?", + "expected_skill": "omniverse-usd-performance-tuning", + "expected_script": null, + "ground_truth": "A Scene Optimizer operation can return success while changing nothing (zero prims affected). The agent should detect a successful-but-no-op result by comparing before/after metrics, report it honestly as neutral, and not claim an improvement the metrics do not support.", + "expected_behavior": [ + "Reads the omniverse-usd-performance-tuning SKILL.md.", + "Compares before/after metrics rather than trusting the operation's success status alone.", + "Recognizes a success-but-zero-work result (no prims or meshes changed) and reports it as neutral.", + "Does not present an unchanged stage as an improvement in the optimization report." + ] } ] } diff --git a/skills/omniverse-usd-performance-tuning/references/compare-profiles.md b/skills/omniverse-usd-performance-tuning/references/compare-profiles.md index 2a4649ff..ad8575aa 100644 --- a/skills/omniverse-usd-performance-tuning/references/compare-profiles.md +++ b/skills/omniverse-usd-performance-tuning/references/compare-profiles.md @@ -36,13 +36,15 @@ workflow reference remains Report absolute values and percentages together. A neutral result is not a failure; it means the measured scene did not materially change for that metric. -## Structural-Only Verdict +## Structural-Only Runs -`verdict: structural-only` is allowed only when the run used quick structural -signals and no meaningful before/after timing or frame metrics were captured. -The report must say which runtime or access blocker prevented a stronger -performance verdict and must recommend the next profile capture needed to -graduate the verdict. +When the run used quick structural signals and no meaningful before/after timing +or frame metrics were captured, set `workflow_mode: structural_only` in the +report — do **not** invent a verdict value. The `verdict` stays within its enum +(`improved | neutral | regressed | mixed`); use `neutral` when no measured metric +materially changed. The report's `notes` field must say which runtime or access +blocker prevented a stronger performance verdict and must recommend the next +profile capture needed to graduate it. ## Terminal Report Requirement diff --git a/skills/omniverse-usd-performance-tuning/references/compare-profiles/README.md b/skills/omniverse-usd-performance-tuning/references/compare-profiles/README.md index cfda75c8..22f0b74b 100644 --- a/skills/omniverse-usd-performance-tuning/references/compare-profiles/README.md +++ b/skills/omniverse-usd-performance-tuning/references/compare-profiles/README.md @@ -160,7 +160,7 @@ The full flow with profiling: omniverse-usd-performance-tuning → profile-stage (BASELINE) → usd-structure-assessment -→ usd-validation-runner (master router; uses skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/validation-scoping.md for tier detail) +→ usd-validation-runner (master router; uses skills/omniverse-usd-performance-tuning/references/usd-validation-runner/README.md for tier detail and selected-probe policy) → restructure-decision (Phase 2e gate) → instancing-readiness (if applicable) → SO operations / instancing diff --git a/skills/omniverse-usd-performance-tuning/references/operations/EXECUTION.md b/skills/omniverse-usd-performance-tuning/references/operations/EXECUTION.md index d0e86acb..5caad2dc 100644 --- a/skills/omniverse-usd-performance-tuning/references/operations/EXECUTION.md +++ b/skills/omniverse-usd-performance-tuning/references/operations/EXECUTION.md @@ -19,6 +19,12 @@ plan. Detailed executable guidance lives in the nested `so-run-operations` references; this page gives repo-root agents enough shape to avoid wrong turns before entering the skill bundle. +Use setup preflight plus live `sceneOptimizer.operationsAvailable` before +execution. Per-operation files are routing stubs; upstream `usd-optimize` docs +own parameters and defaults. Local invocation mechanics live in +`../so-run-operations/references/invocation.md`; do not invent or duplicate +Python call shapes here. + ## Optional Helper Wrapper Shape Use these wrapper paths only when the selected Scene Optimizer environment or @@ -101,7 +107,10 @@ dependency-bound, or observed resource pressure makes parallelism unsafe. When targets include prototypes and non-prototype assets, run prototypes first. Parallelize within each dependency group when resources allow. Prototype changes propagate to instances, so instance-site work before prototype work -wastes runtime and can produce misleading metrics. +wastes runtime and can produce misleading metrics. If the batch manifest +contains `target_class: "assembly_root"` with retained meshes, process it as a +non-prototype mesh target before final stage-level cleanup; do not reduce it to +`pruneLeaves`/`computeExtents` only. For each target, include a stable hash of the absolute input path in optimized USD, summary, and log filenames. After every batch, verify that produced output @@ -115,3 +124,14 @@ Scene Optimizer mutates the opened stage in memory. Default to exporting an optimized `.usdc` output under `output_path`. Use in-place `Save()` only for newly created layers or explicitly approved source edits, and use flattened stage export only when the user asks for a flattened deliverable. + +## Rules + +- Confirm bounded-loss/destructive operations before mutation. +- Use selected targets from SA/validation evidence. +- Store config, log, output stage, and summary artifacts. +- If helper wrappers exist in the selected environment they may be used; + otherwise use the Python/API executor from the invocation reference. +- Do not pass a plain `pxr.Usd.Stage` directly to Scene Optimizer operation + APIs. Attach it to `ExecutionContext` or use the standalone JSON helper as + described in the invocation reference. diff --git a/skills/omniverse-usd-performance-tuning/references/operations/_curation.json b/skills/omniverse-usd-performance-tuning/references/operations/_curation.json index 635e2516..7055c9a3 100644 --- a/skills/omniverse-usd-performance-tuning/references/operations/_curation.json +++ b/skills/omniverse-usd-performance-tuning/references/operations/_curation.json @@ -61,7 +61,7 @@ }, "removeUnusedUVs": { "status": "canonical", - "rationale": "canonical: C1+C2: lossless mesh-cleanup op surfaced via pipelines.md cad-bim-cleanup pipeline; routing skill points at pipelines.md rather than naming the op directly.", + "rationale": "canonical: C1+C2: lossless mesh-cleanup op surfaced as a local routing key in pipelines.md for CAD/BIM cleanup (named pipeline recipes live upstream); routing skill points at pipelines.md rather than naming the op directly.", "wired_into": [ "skills/omniverse-usd-performance-tuning/references/so-run-operations/references/pipelines.md" ] @@ -122,8 +122,11 @@ }, "findCoincidingGeometry": { "status": "analysis", - "rationale": "analysis: A1+A2: lossless coincidence analyzer; candidate for so-interpret-validators wiring.", - "wired_into": [] + "rationale": "analysis: A1+A2: lossless coincidence analyzer; wired into the so-interpret-validators interpretation map (SceneOptimizerCoincidingGeometryChecker -> spatial_coinciding) and the workflow analysis-op guidance. Prefer deduplicateGeometry before destructive deletion.", + "wired_into": [ + "skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-interpret-validators/references/rule-reference.md", + "skills/omniverse-usd-performance-tuning/references/workflow.md" + ] }, "findOccludedMeshes": { "status": "canonical", @@ -136,12 +139,15 @@ }, "findFlatHierarchies": { "status": "analysis", - "rationale": "analysis: A1+A2: lossless hierarchy-shape finder; candidate for so-interpret-validators wiring.", - "wired_into": [] + "rationale": "analysis: A1+A2: lossless hierarchy-shape finder; wired into the so-interpret-validators interpretation map (SceneOptimizerFlatHierarchiesChecker -> flattenHierarchy) and the workflow analysis-op guidance.", + "wired_into": [ + "skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-interpret-validators/references/rule-reference.md", + "skills/omniverse-usd-performance-tuning/references/workflow.md" + ] }, "fitPrimitives": { "status": "canonical", - "rationale": "canonical: C1+C2: bounded-loss geometry op invoked in cad-bim-cleanup pipeline (pipelines.md); critical for CAD/BIM/MEP scenes. Requires user confirmation (replaces meshes with analytic primitives).", + "rationale": "canonical: C1+C2: bounded-loss geometry op surfaced as a local routing key in pipelines.md for CAD/BIM cleanup (named pipeline recipes live upstream); critical for CAD/BIM/MEP scenes. Requires user confirmation (replaces meshes with analytic primitives).", "wired_into": [ "skills/omniverse-usd-performance-tuning/references/so-run-operations/references/pipelines.md", "skills/omniverse-usd-performance-tuning/references/operations/fitPrimitives.md" @@ -262,7 +268,7 @@ }, "sparseMeshes": { "status": "specialty", - "rationale": "specialty: S2: validator-finding evidence (SceneOptimizerSparseMeshChecker T2 in rule-reference.md) wires it into the so-interpret-validators chain. Analysis-only op that classifies meshes by spatial density and surfaces split / dice candidates; surfaces from validation-scoping.md Tier 3 (outlier_extent-flagged assets). Outside the default canonical flow but actionable when the checker fires.", + "rationale": "specialty: S2: validator-finding evidence (SceneOptimizerSparseMeshChecker T2 in rule-reference.md) wires it into the so-interpret-validators chain. Analysis-only op that classifies meshes by spatial density and surfaces split / dice candidates; surfaces from usd-validation-runner Tier 3 policy (outlier_extent-flagged assets). Outside the default canonical flow but actionable when the checker fires.", "wired_into": [ "skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-interpret-validators/references/rule-reference.md", "skills/omniverse-usd-performance-tuning/references/workflow.md" @@ -292,12 +298,15 @@ }, "findOverlappingMeshes": { "status": "analysis", - "rationale": "analysis: A1+A2: lossless overlap analyzer; candidate for future wiring after live operationsAvailable confirms support.", - "wired_into": [] + "rationale": "analysis: A1+A2: lossless overlap analyzer; wired into the so-interpret-validators interpretation map (SceneOptimizerFindOverlappingMeshesChecker -> spatial_overlapping) and the workflow analysis-op guidance.", + "wired_into": [ + "skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-interpret-validators/references/rule-reference.md", + "skills/omniverse-usd-performance-tuning/references/workflow.md" + ] }, "shrinkwrap": { "status": "documentary", "rationale": "documentary: D1+D2+D3: specialty geometry op; use only after live operationsAvailable confirms support.", "wired_into": [] } -} \ No newline at end of file +} diff --git a/skills/omniverse-usd-performance-tuning/references/operations/fitPrimitives.md b/skills/omniverse-usd-performance-tuning/references/operations/fitPrimitives.md index 327e622e..442fdae3 100644 --- a/skills/omniverse-usd-performance-tuning/references/operations/fitPrimitives.md +++ b/skills/omniverse-usd-performance-tuning/references/operations/fitPrimitives.md @@ -9,7 +9,7 @@ requires_confirmation: true risk_class: high args_count: 20 requires_mesh: true -pipelines: [cad-bim-cleanup] +pipelines: [] keywords: [fit, primitive, cube, sphere, cylinder, approximate] since_version: 2026-02-11T07:51:19Z requires_extension: omni.scene.optimizer.core diff --git a/skills/omniverse-usd-performance-tuning/references/optimization-report/README.md b/skills/omniverse-usd-performance-tuning/references/optimization-report/README.md index 13398776..9f0af6b3 100644 --- a/skills/omniverse-usd-performance-tuning/references/optimization-report/README.md +++ b/skills/omniverse-usd-performance-tuning/references/optimization-report/README.md @@ -27,8 +27,9 @@ Before generating the report, re-read and confirm: `references/report-templates/render_preview.py` with `--fixture` and `--output`. Never hand-write HTML. Never use LLM-generated HTML. - [ ] **JSON conforms to schema** — read - `scripts/optimization-report.schema.json` in full before writing. Do not - guess field names from memory. + `scripts/optimization-report.schema.json` in full before writing, then + validate the finished report with `python3 scripts/validate_report.py `. + Do not guess field names from memory. - [ ] **Runtime context copied verbatim** from `setup-preflight.json`. - [ ] **Score computed deterministically** — weighted average of metric groups, not hand-assigned. @@ -221,74 +222,134 @@ Structure: "issues": 1525, "notes": "Candidates for pruneLeaves" } - ] + ], + "target_coverage": { + "complete": true, + "source_manifests": ["apply-restructure-manifest.json"], + "entries": [ + { + "path": "/out/prototypes/discipline.usd", + "role": "prototype", + "mesh_count": 318, + "disposition": "optimized", + "operations": ["pruneLeaves", "deduplicateGeometry"] + } + ] + } } ``` -## Markdown template - -```markdown -## USD Performance Tuning Report — {asset_name} - -Stage Optimization Score: {optimization_score}/10 ({score_label}) -Verdict: {verdict} - -### Output File -{output_path} - -### Reasoning +## Phase-4 target coverage (completion gate) -{reasoning} +The required top-level `target_coverage` block is the Phase-4 analogue of the +validation report's `coverage_ledger`: it proves every mesh-optimization target +was resolved instead of silently dropped. It is structurally flat — `entries[]` +plus a `complete` boolean — to mirror the validation report rather than nesting a +`phase4` wrapper. -### Runtime Context - -| Component | Value | -|---|---| -| Kit application | {runtime_context.kit.application} {runtime_context.kit.version} | -| Kit path | {runtime_context.kit.path} | -| Kit build | {runtime_context.kit.build} | -| Scene Optimizer | {runtime_context.sceneOptimizer.extension} {runtime_context.sceneOptimizer.version} | -| Asset Validator | {runtime_context.assetValidator.package} {runtime_context.assetValidator.version} (via {runtime_context.assetValidator.source}) | - ---- - -### Before / After at a Glance - -| Metric | Before | After | Change | -|---|---|---|---| -| {for each metric} | - -### Stage Impact Areas +```json +"target_coverage": { + "complete": true, + "source_manifests": ["apply-restructure-manifest.json"], + "entries": [ + { + "path": "/out/prototypes/rack_unit.usd", + "role": "prototype", + "mesh_count": 412, + "disposition": "optimized", + "operations": ["meshCleanup", "decimateMeshes"] + }, + { + "path": "/out/assembly.usdc", + "role": "assembly_root", + "mesh_count": 0, + "disposition": "skipped_zero_meshes" + } + ] +} +``` -| Area | Score | Status | Notes | -|---|---:|---|---| -| Composition Load | {score}/10 | measured | {summary} | -| Composition Complexity | {score}/10 | measured | {summary} | -| Instancing | {score}/10 | measured | {summary} | -| Validation | {score}/10 | measured | {summary} | +- `role` is one of `assembly_root | prototype | shared_layer | loadable_subasset` + (the restructure roles) or `monolith` for a non-restructured optimize-as-is + target (N=1). +- `disposition` is one of `optimized | skipped_zero_meshes | skipped_user_declined | blocked`. + `complete` is true only when every entry is one of the first three; a `blocked` + or unresolved target keeps `complete` false and the report is not final. +- `skipped_zero_meshes` is valid only when `mesh_count == 0` (the default-predicate + count). A non-zero target cannot be skipped. +- A diagnosis-only / optimize-as-is run with no Phase-4 work is valid with + `entries: []` and `complete: true`. A `monolith`-only run records its single + target and needs no manifest. + +Because a report cannot self-attest coverage of a target it never enumerated, +reconciliation against the upstream apply-restructure manifest(s) is **mandatory +once a restructure happened**: whenever any entry has a restructure role, record +the manifest path(s) in `target_coverage.source_manifests[]` (one per iteration). +`validate_report.py` auto-loads them (resolved relative to the report) and also +accepts `--manifest`, then reconciles `target_coverage` against the **union** of +every iteration's `phase4_targets[]`: -### Runtime Profiling +```bash +python3 scripts/validate_report.py \ + [--manifest ] \ + [--manifest ...] +``` -Runtime metrics such as RAM, VRAM, FPS, frame time, shader cost, and renderer -activity are not part of the Stage Optimization Score. Use Omniperf or an -equivalent runtime profiling dashboard for those measurements. +The gate exits non-zero if a restructure report records/supplies no manifest, a +planned target is uncovered, a covered target is absent from every manifest, a +disposition is unresolved, or a `skipped_zero_meshes` target has a manifest +`mesh_count > 0`. + +## Runtime profiling handoff (producer contract) + +Runtime metrics — RAM, VRAM, FPS, frame time, Hydra sync, RTX render time, +draw-call counts, shader-compile time — are deliberately **not** inputs to the +Stage Optimization Score. They come from an external runtime profiler such as +NVIDIA/omniperf. This report is the consumer; the producer contract is: + +- The runtime profiler writes its own artifact (JSON and/or dashboard) outside + this report. Record its path in `runtime_profiling.artifact_path` and any + dashboard link in `runtime_profiling.dashboard_url`. +- Set `runtime_profiling.status` to `not_run` (no runtime profiling happened), + `external` (an artifact/dashboard exists elsewhere and is linked), or + `attached` (the profiler's summary is reproduced in `summary`). +- Put a one-line before/after runtime summary in `runtime_profiling.summary` + and any measurement caveat in `runtime_profiling.caveat`. +- Keep these values out of `metric_groups[]` and `metrics[]` so the stage score + stays composition-only while the runtime numbers remain visible in the report. ---- +## Markdown template -### Operations Performed +The Markdown summary is **generated from the report JSON**, never hand-written. +There is exactly one canonical Markdown layout: the committed template +`references/report-templates/optimization-report.md.template` (Jinja-style, +double-brace placeholders, with `{{ executive_summary }}`, a Runtime Context +table, and `{% for %}` loops over `metric_groups`, `metrics`, `operations`, and +`validators`). Its field names track `scripts/optimization-report.schema.json` +(including `executive_summary` and the top-level `notes`). -| # | Operation | Method | Result | -|---|---|---|---| -| {for each operation} | +Render it with the **same committed renderer** used for the HTML report — just +point `--template` at the Markdown template instead of the default HTML one: ---- +```bash +python3 references/report-templates/render_preview.py \ + --fixture /_optimization_report.json \ + --template references/report-templates/optimization-report.md.template \ + --output /_optimization_report.md +``` -### Validators Attempted / Result +**Do not hand-fill a Markdown report** and do not paste a template body into +chat as a fill-in form — that is the dual-maintenance drift this contract exists +to prevent, and it violates the SKILL-level "never hand-write the report" rule. +The JSON is the source of truth; the `.md.template` above is the only place the +Markdown layout is maintained. -| Validator | Result | -|---|---| -| {for each validator} | -``` +The renderer emits the following sections in order (rendered-output preview — +illustrative only, **not** a hand-fill target): the title with the asset name; +a Stage Optimization Score / verdict / generated-timestamp / output line; the +executive summary; Reasoning; a Runtime Context table; Stage Impact Areas; a +Runtime Profiling note (plus a Runtime Profiling table when handoff fields are +present); Metric Evidence; Operations; and Validators. ## Rules @@ -302,14 +363,20 @@ equivalent runtime profiling dashboard for those measurements. > canonical shape and validate your output against the `required` and `items` > blocks before emitting. The schema rejects extra keys in `metric_groups[]`, > `metrics[]`, `operations[]`, `validators[]`, and `artifacts`; update the -> schema first if a new report field is genuinely needed. Conformance failures -> are caught by `scored_static_html_report_required` in runtime checks. +> schema first if a new report field is genuinely needed. Validate the finished +> JSON by running `python3 scripts/validate_report.py ` (committed, +> dependency-free) before treating the report as final; the repository test +> `test_committed_report_fixtures_conform_to_schema` holds the bundled fixtures +> to this same schema. - Always save the JSON report — it is the structured record of what happened. - Always present the markdown to the user in chat. -- Always produce the static HTML report when writing report artifacts. In - runtime checks, include `scored_static_html_report_required` in - `phase_guardrails` when planning this final report contract. +- Always produce the static HTML report when writing report artifacts, and run + `python3 scripts/validate_report.py` on the report JSON before finishing. + `scored_static_html_report_required` is an agent-asserted planning guardrail — + list it in `phase_guardrails` when planning this final report contract — not an + automated gate. The automated conformance check is `python3 scripts/validate_report.py` + plus the repository schema test. - Always title the reader-facing report **USD Performance Tuning Report**. - Always include a dedicated `Reasoning` section with one to two concise paragraphs explaining why the selected optimizations fit the evidence. diff --git a/skills/omniverse-usd-performance-tuning/references/optimization-report/references/optimization-report-template.md b/skills/omniverse-usd-performance-tuning/references/optimization-report/references/optimization-report-template.md index ed1e80b3..afc432e5 100644 --- a/skills/omniverse-usd-performance-tuning/references/optimization-report/references/optimization-report-template.md +++ b/skills/omniverse-usd-performance-tuning/references/optimization-report/references/optimization-report-template.md @@ -22,16 +22,18 @@ Required top-level fields: | `asset_name` | string | Phase 0 | Set early; the basename of the input asset usually suffices. | | `input_path` | string | Phase 0 | Optional in schema, but capture it for traceability. | | `output_path` | string | Phase 5 | Path to the optimized stage root from Phase 5d (or `null` for diagnosis-only / structural-only path). | -| `timestamp` | string (ISO 8601) | Phase 6e | Set when the report writes. | -| `verdict` | enum: `improved \| neutral \| regressed \| mixed` | Phase 6c | From `compare-profiles`. Use `structural-only` semantically when SO was unavailable (degraded path) and document it in `notes`. | -| `optimization_score` | number 0-10 | Phase 6e | Stage Optimization Score. Compute deterministically as `round(sum(group.score * group.weight) / sum(group.weight), 1)` across scored stage/composition groups only. Exclude `score=null` and `weight=0` groups. Runtime metrics are not score inputs. | -| `score_scope` | enum: `stage_optimization` | Phase 6e | Makes the score scope explicit so readers do not confuse it with full runtime performance. | -| `score_label` | enum | Phase 6e | Human score band from `optimization_score`: `excellent >= 9.0`, `strong >= 7.5`, `moderate >= 5.5`, `neutral >= 4.5`, `mixed >= 2.5`, `regressed < 2.5`. | -| `reasoning` | string | Phase 6e | One to two paragraphs explaining why the agent chose this optimization approach for the asset, based on evidence and tradeoffs. | +| `timestamp` | string (ISO 8601) | Phase 6d | Set when the report writes. | +| `verdict` | enum: `improved \| neutral \| regressed \| mixed` | Phase 6c | From `compare-profiles`. Stays in this enum in every mode; use `neutral` when no metrics changed. Express degraded/no-op runs via `workflow_mode`, not new verdict values. | +| `workflow_mode` | enum: `full \| structural_only \| no_op` | Phase 6d | Optional (default `full`). `structural_only` when SO was unavailable and only USD-structural work ran; `no_op` when SA reported `already_optimized`. | +| `notes` | string | any phase | Optional. Caveats the verdict/score cannot capture: degraded-path reason, runtime/access blocker, or the next profile capture needed to graduate the verdict. | +| `optimization_score` | number 0-10 | Phase 6d | Stage Optimization Score. Compute deterministically as `round(sum(group.score * group.weight) / sum(group.weight), 1)` across scored stage/composition groups only. Exclude `score=null` and `weight=0` groups. Runtime metrics are not score inputs. | +| `score_scope` | enum: `stage_optimization` | Phase 6d | Makes the score scope explicit so readers do not confuse it with full runtime performance. | +| `score_label` | enum | Phase 6d | Human score band from `optimization_score`: `excellent >= 9.0`, `strong >= 7.5`, `moderate >= 5.5`, `neutral >= 4.5`, `mixed >= 2.5`, `regressed < 2.5`. | +| `reasoning` | string | Phase 6d | One to two paragraphs explaining why the agent chose this optimization approach for the asset, based on evidence and tradeoffs. | | `measurement_context` | object | Phases 0, 1a, 6a | Context for stage/composition measurements: runtime, cache policy, sample count, stage-open method. | -| `runtime_profiling` | object | Phase 6e | Optional Omniperf/runtime-profiler handoff for RAM, VRAM, FPS, frame time, shader, renderer, and GPU metrics. | -| `metric_groups[]` | array | Phase 6e | Stage headline areas such as composition load, structure, instancing, storage footprint, and validation. | -| `artifacts` | object | Phase 6e | Paths to generated JSON, Markdown, and static HTML reports. | +| `runtime_profiling` | object | Phase 6d | Optional Omniperf/runtime-profiler handoff for RAM, VRAM, FPS, frame time, shader, renderer, and GPU metrics. | +| `metric_groups[]` | array | Phase 6d | Stage headline areas such as composition load, structure, instancing, storage footprint, and validation. | +| `artifacts` | object | Phase 6d | Paths to generated JSON, Markdown, and static HTML reports. | | `metrics[]` | array | Phases 1a + 6a | Each metric: `name`, `before`, `after`, `change_pct`, `verdict`. | | `operations[]` | array | Phases 4 + 5 | Each op: `order`, `name`, `method`, `result`. | | `validators[]` | array | Phases 2c + 6b | Each validator entry: `name`, `issues`, `notes`; `issues` is the count of reported findings for that row. | @@ -64,8 +66,8 @@ in `runtime_profiling`, ideally via Omniperf dashboard/artifacts. ### Phase 2 - Composition / discovery / restructure decision -- [ ] `validators[]` - first entries (validator name + issue count from Phase 2c sweep). One row per validator that ran. -- [ ] If user takes the "exit" branch at Phase 2e gate: skip to Phase 6e and write a structural-only report. +- [ ] `validators[]` - first entries (validator name + issue count from Phase 2c selected probes). One row per validator that ran. +- [ ] If user takes the "exit" branch at Phase 2e gate: skip to Phase 6d and write a diagnosis-only report (`output_path: null`, empty `operations[]`). ### Phase 3 - Stage-level instancing @@ -96,7 +98,7 @@ in `runtime_profiling`, ideally via Omniperf dashboard/artifacts. - [ ] `reasoning` - one to two concise paragraphs explaining the chosen optimization strategy and tradeoffs. - [ ] `runtime_profiling` - point to Omniperf/runtime-profiler artifacts if available, or mark as `not_run` with a recommendation. - [ ] `artifacts` - include the JSON, Markdown, and HTML report paths. -- [ ] Generate HTML by running `python3 references/report-templates/render_preview.py` (mandatory — do NOT hand-write HTML). See `references/optimization-report/README.md § HTML Generation`. +- [ ] Generate HTML by running `python3 references/report-templates/render_preview.py --fixture --output ` (mandatory — do NOT hand-write HTML, and never run it argless: that renders the committed design fixture, not your report). See `references/optimization-report/README.md § HTML Generation`. Do not emit the final report as a normal completed optimization if Phase 6a or Phase 6b artifacts are missing. Either run the missing phase, or record the @@ -110,7 +112,7 @@ unclaimed, and keep the verdict no stronger than the remaining evidence allows. When SO is unavailable and the user declines setup: - `output_path` may be `null` (no Phase 4 ops were applied, no Phase 5 ref-rewrite happened). -- `verdict` should be set to whatever applies; if no metrics changed, `neutral` is acceptable. Note structural-only status in the `validators[]` notes or a top-level `notes` field. +- `verdict` stays in its enum; if no metrics changed, use `neutral`. Set `workflow_mode: structural_only` and record the SO-unavailable reason in the top-level `notes` field. - `operations[]` may be empty. - `validators[]` should still contain the Phase 2c USD-stack findings. - `runtime_profiling.status` should usually be `not_run` unless an external Omniperf/runtime-profiler artifact is attached. @@ -147,11 +149,16 @@ the caveat above attached. ### Iteration (Phase 7) -When the agent loops back from Phase 7 (optional): +When the agent loops back from Phase 7: +- Default broad optimization to 3 scoped iterations unless the user opts out, + requests a quick pass, or stop criteria apply. +- Write an interim report/update after each iteration before continuing. - KEEP the `before` values from the FIRST baseline. Do NOT re-baseline. - Append to `operations[]` with continued `order` numbering across iterations. - Update `after` values from each new Phase 6a re-profile. +- Reuse prior validator evidence unless the next pass needs a narrower targeted + or delta probe; expanded validation scope requires explicit approval. - The final `verdict` reflects the cumulative comparison (first baseline vs latest after). ### Diagnosis-only diff --git a/skills/omniverse-usd-performance-tuning/references/optimization-report/scripts/optimization-report.schema.json b/skills/omniverse-usd-performance-tuning/references/optimization-report/scripts/optimization-report.schema.json index 544d3b71..34f2c8e7 100644 --- a/skills/omniverse-usd-performance-tuning/references/optimization-report/scripts/optimization-report.schema.json +++ b/skills/omniverse-usd-performance-tuning/references/optimization-report/scripts/optimization-report.schema.json @@ -18,8 +18,10 @@ "metric_groups", "metrics", "operations", - "validators" + "validators", + "target_coverage" ], + "additionalProperties": false, "properties": { "asset_name": { "type": "string", @@ -30,8 +32,8 @@ "description": "Path to the original input stage." }, "output_path": { - "type": "string", - "description": "Path to the optimized output stage." + "type": ["string", "null"], + "description": "Path to the optimized output stage; null for diagnosis-only, no-op, or structural-only runs where no optimized stage was written." }, "timestamp": { "type": "string", @@ -41,7 +43,16 @@ "verdict": { "type": "string", "enum": ["improved", "neutral", "regressed", "mixed"], - "description": "Overall optimization verdict." + "description": "Overall optimization verdict from compare-profiles. Stays within this enum regardless of workflow_mode; use 'neutral' when no metrics changed. Degraded (Scene Optimizer unavailable) and no-op runs are expressed via workflow_mode, not by inventing new verdict values." + }, + "workflow_mode": { + "type": "string", + "enum": ["full", "structural_only", "no_op"], + "description": "Execution mode for this run. 'full' = optimization/mutation ran (default when omitted); 'structural_only' = Scene Optimizer was unavailable so only USD-structural work ran and no mesh operations executed; 'no_op' = no optimization was needed (e.g., structure assessment reported already_optimized). The verdict stays in its own enum in every mode." + }, + "notes": { + "type": "string", + "description": "Free-form caveats the verdict and score cannot capture on their own: degraded-path explanation, the runtime or access blocker that prevented a stronger verdict, or the next profile capture needed to graduate it." }, "runtime_context": { "type": "object", @@ -121,6 +132,10 @@ "enum": ["excellent", "strong", "moderate", "neutral", "mixed", "regressed"], "description": "Human-readable score band derived from optimization_score: excellent >= 9.0; strong >= 7.5 and < 9.0; moderate >= 5.5 and < 7.5; neutral >= 4.5 and < 5.5; mixed >= 2.5 and < 4.5; regressed < 2.5." }, + "executive_summary": { + "type": "string", + "description": "Optional short reader-facing summary rendered near the top of the Markdown/HTML report." + }, "reasoning": { "type": "string", "description": "One to two concise paragraphs explaining why the agent chose the specific stage optimization approach for this asset, including the evidence that drove the choice and any tradeoffs." @@ -252,6 +267,58 @@ "notes": { "type": "string" } } } + }, + "target_coverage": { + "type": "object", + "description": "Phase-4 mesh-optimization coverage ledger, structurally parallel to the validation report's coverage_ledger. entries[] must cover the UNION of every iteration's apply-restructure manifest phase4_targets[]; complete is true only when every entry reached a resolved disposition. validate_report.py reconciles this against the upstream manifest(s) so a target that was never enumerated (e.g. an assembly_root dropped from a later iteration's manifest) fails closed instead of silently passing. Reconciliation is NOT optional once a restructure happened: if any entry has a restructure role (assembly_root | prototype | shared_layer | loadable_subasset) the gate requires a manifest, supplied via --manifest or recorded in source_manifests[]. A diagnosis-only / optimize-as-is-monolith run (no restructure roles) is manifest-free; an empty Phase-4 run is valid with entries: [] and complete: true.", + "required": ["complete", "entries"], + "additionalProperties": false, + "properties": { + "complete": { + "type": "boolean", + "description": "True only when every entry's disposition is one of optimized | skipped_zero_meshes | skipped_user_declined. A 'blocked' or unresolved entry keeps this false and the report is not final." + }, + "source_manifests": { + "type": "array", + "items": { "type": "string" }, + "description": "Path(s) to the apply-restructure manifest(s) this coverage was reconciled against, one per restructure iteration. Recorded so validate_report.py can auto-load and reconcile them (resolved relative to the report file when not absolute), making reconciliation fail-closed rather than dependent on the operator remembering --manifest. Required in effect whenever any entry has a restructure role; omit for monolith/diagnosis runs." + }, + "entries": { + "type": "array", + "items": { + "type": "object", + "required": ["path", "role", "mesh_count", "disposition"], + "additionalProperties": false, + "properties": { + "path": { + "type": "string", + "description": "Phase-4 target file; reconciliation key against apply-restructure manifest phase4_targets[].path." + }, + "role": { + "type": "string", + "enum": ["assembly_root", "prototype", "shared_layer", "loadable_subasset", "monolith"], + "description": "Target kind. The restructure roles (assembly_root | prototype | shared_layer | loadable_subasset) trigger mandatory manifest reconciliation. 'monolith' is the non-restructured optimize-as-is target (N=1) and does not require a manifest." + }, + "mesh_count": { + "type": "integer", + "minimum": 0, + "description": "Default-predicate mesh count for this target (echoed from the manifest's authoritative count). disposition 'skipped_zero_meshes' is valid only when this is 0." + }, + "disposition": { + "type": "string", + "enum": ["optimized", "skipped_zero_meshes", "skipped_user_declined", "blocked"], + "description": "optimized = the per-target mesh op chain ran; skipped_zero_meshes = no meshes to optimize (requires mesh_count == 0); skipped_user_declined = user opted out of optimizing this target; blocked = could not be processed (keeps complete=false)." + }, + "operations": { + "type": "array", + "items": { "type": "string" }, + "description": "Optional: the mesh op chain applied to this target, for traceability." + }, + "notes": { "type": "string" } + } + } + } + } } } } diff --git a/skills/omniverse-usd-performance-tuning/references/optimization-report/scripts/validate_report.py b/skills/omniverse-usd-performance-tuning/references/optimization-report/scripts/validate_report.py new file mode 100644 index 00000000..ce7eb1f8 --- /dev/null +++ b/skills/omniverse-usd-performance-tuning/references/optimization-report/scripts/validate_report.py @@ -0,0 +1,338 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Validate a USD Performance Tuning report against optimization-report.schema.json. + +Deterministic local validation with no third-party runtime dependencies. The +agent (or CI) should run this before treating a report as final, so an +out-of-enum verdict, a missing required field, or an unexpected array-item key +is caught instead of shipping a schema-invalid report. + +Implements the JSON Schema draft-07 subset this schema uses: type (including +type unions like ["string", "null"]), enum, required, properties, +additionalProperties=false, items, minimum, and maximum. + +Phase-4 target coverage gate +---------------------------- +Schema validation alone cannot catch a Phase-4 target that was never enumerated +in the report (the failure mode where an assembly_root remainder is silently +left un-optimized). A report's ``target_coverage.complete`` flag is self-attested +by the report author, so the gate reconciles ``target_coverage`` against the +upstream apply-restructure manifest(s): the report must cover the UNION of every +iteration's ``phase4_targets[]``, every disposition must be resolved, and +``skipped_zero_meshes`` is accepted only when the manifest's authoritative +``mesh_count`` for that target is 0. + +Reconciliation is fail-closed, not opt-in: when any coverage entry has a +restructure role (assembly_root | prototype | shared_layer | loadable_subasset) +a manifest is REQUIRED. Manifests are taken from ``--manifest`` and/or the +report's own ``target_coverage.source_manifests[]`` (auto-loaded relative to the +report), so a restructure report cannot pass merely because the operator forgot +the flag. Monolith/diagnosis runs (no restructure roles) stay manifest-free. + +Usage: + python3 validate_report.py [--schema ] \\ + [--manifest ...] +Exit code 0 when the report conforms and the coverage gate passes, 1 otherwise. +""" +from __future__ import annotations + +import argparse +import json +from pathlib import Path +from typing import Any + +DEFAULT_SCHEMA = Path(__file__).resolve().parent / "optimization-report.schema.json" + +#: A Phase-4 target is "resolved" only with one of these dispositions. ``blocked`` +#: (or a target with no entry at all) keeps ``target_coverage.complete`` false and +#: the report non-final — mirroring the validation report's RESOLVED_STATUSES. +PHASE4_RESOLVED_DISPOSITIONS = frozenset( + {"optimized", "skipped_zero_meshes", "skipped_user_declined"} +) +RESTRUCTURE_TARGET_CLASSES = frozenset( + {"prototype", "shared_layer", "loadable_subasset", "assembly_root"} +) +#: Coverage-entry roles that mean "a restructure happened", so a manifest is +#: mandatory and reconciliation is not optional. The ``monolith`` role (an +#: optimize-as-is N=1 target) and an empty ledger stay manifest-free. +RESTRUCTURE_ROLES = frozenset( + {"assembly_root", "prototype", "shared_layer", "loadable_subasset"} +) + + +def _type_ok(instance: Any, type_name: str) -> bool: + if type_name == "object": + return isinstance(instance, dict) + if type_name == "array": + return isinstance(instance, list) + if type_name == "string": + return isinstance(instance, str) + if type_name == "number": + return isinstance(instance, (int, float)) and not isinstance(instance, bool) + if type_name == "integer": + return isinstance(instance, int) and not isinstance(instance, bool) + if type_name == "boolean": + return isinstance(instance, bool) + if type_name == "null": + return instance is None + return True + + +def _validate(instance: Any, schema: dict, path: str, errors: list[str]) -> None: + declared_type = schema.get("type") + if declared_type is not None: + candidates = declared_type if isinstance(declared_type, list) else [declared_type] + if not any(_type_ok(instance, name) for name in candidates): + got = "null" if instance is None else type(instance).__name__ + errors.append(f"{path}: expected type {candidates}, got {got}") + return + + if "enum" in schema and instance not in schema["enum"]: + errors.append(f"{path}: {instance!r} is not one of {schema['enum']}") + + if isinstance(instance, (int, float)) and not isinstance(instance, bool): + if "minimum" in schema and instance < schema["minimum"]: + errors.append(f"{path}: {instance} is below minimum {schema['minimum']}") + if "maximum" in schema and instance > schema["maximum"]: + errors.append(f"{path}: {instance} is above maximum {schema['maximum']}") + + if isinstance(instance, dict): + properties = schema.get("properties", {}) + for required_key in schema.get("required", []): + if required_key not in instance: + errors.append(f"{path}: missing required property '{required_key}'") + allow_additional = schema.get("additionalProperties", True) + for key, value in instance.items(): + if key in properties: + _validate(value, properties[key], f"{path}.{key}", errors) + elif allow_additional is False: + errors.append(f"{path}: unexpected property '{key}'") + + if isinstance(instance, list) and "items" in schema: + for index, item in enumerate(instance): + _validate(item, schema["items"], f"{path}[{index}]", errors) + + +def validate_report(report: Any, schema: dict | None = None) -> list[str]: + """Return a list of schema-violation messages; empty list means the report conforms.""" + if schema is None: + schema = json.loads(DEFAULT_SCHEMA.read_text(encoding="utf-8")) + errors: list[str] = [] + _validate(report, schema, "$", errors) + return errors + + +def validate_manifest_structure(manifest: Any) -> list[str]: + """Enforce the load-bearing apply-restructure manifest invariants. + + Independent of the JSON-Schema walker so the rules hold without ``jsonschema``: + a ``mode=restructure`` manifest must carry a non-empty ``phase4_targets[]``, + and every target must declare an integer ``mesh_count >= 0`` (the authoritative + default-predicate count the coverage gate keys on). + """ + errors: list[str] = [] + if not isinstance(manifest, dict): + return [f"manifest must be an object, got {type(manifest).__name__}"] + mode = manifest.get("mode") + targets = manifest.get("phase4_targets") + if mode == "restructure" and not targets: + errors.append( + "mode=restructure manifest must list a non-empty phase4_targets[] " + "(do not drop the key; an assembly_root with retained meshes must appear)" + ) + for index, target in enumerate(targets or []): + where = f"phase4_targets[{index}]" + path = target.get("path") if isinstance(target, dict) else None + label = f"{where} ({path})" if path else where + if not isinstance(target, dict): + errors.append(f"{where}: must be an object") + continue + if not isinstance(path, str) or not path: + errors.append(f"{where}: missing required 'path'") + target_class = target.get("target_class") + if target_class not in RESTRUCTURE_TARGET_CLASSES: + errors.append( + f"{label}: target_class {target_class!r} not in {sorted(RESTRUCTURE_TARGET_CLASSES)}" + ) + mesh_count = target.get("mesh_count") + if isinstance(mesh_count, bool) or not isinstance(mesh_count, int) or mesh_count < 0: + errors.append( + f"{label}: mesh_count must be an integer >= 0 (authoritative " + f"default-predicate count), got {mesh_count!r}" + ) + return errors + + +def load_recorded_manifests( + report: Any, base_dir: Path +) -> tuple[list[tuple[str, Any]], list[str]]: + """Load the manifests recorded in ``target_coverage.source_manifests[]``. + + Relative paths resolve against ``base_dir`` (the report's directory) so a + report can carry its own provenance and the gate fails closed without the + operator having to remember ``--manifest``. Returns ``(labeled_manifests, + errors)`` where each labeled manifest is ``(source_path, manifest_dict)``. + """ + labeled: list[tuple[str, Any]] = [] + errors: list[str] = [] + coverage = report.get("target_coverage") if isinstance(report, dict) else None + if not isinstance(coverage, dict): + return labeled, errors + for rel in coverage.get("source_manifests", []) or []: + path = Path(rel) + if not path.is_absolute(): + path = base_dir / path + try: + labeled.append((rel, json.loads(path.read_text(encoding="utf-8")))) + except (OSError, json.JSONDecodeError) as exc: + errors.append( + f"target_coverage.source_manifests entry {rel!r} could not be loaded: {exc}" + ) + return labeled, errors + + +def _manifest_targets(manifests: list[Any]) -> dict[str, int | None]: + """Union of every manifest's phase4_targets path -> authoritative mesh_count. + + Multi-iteration runs must reconcile against the UNION: the exact regression + that prompted this gate was iteration 1 listing an assembly_root that + iteration 2's manifest dropped, leaving it uncovered by the final report. + """ + planned: dict[str, int | None] = {} + for manifest in manifests: + for target in manifest.get("phase4_targets", []) or []: + if isinstance(target, dict) and isinstance(target.get("path"), str): + planned[target["path"]] = target.get("mesh_count") + return planned + + +def reconcile_target_coverage(report: Any, manifests: list[Any] | None = None) -> list[str]: + """Gate the report's Phase-4 target_coverage; reconcile against manifest(s). + + Returns violation messages (empty == the gate passes). Always checks the + report's internal consistency (resolved dispositions, the + ``skipped_zero_meshes => mesh_count == 0`` rule, and the ``complete`` flag). + When ``manifests`` are supplied it also asserts the covered set equals the + union of every manifest's ``phase4_targets[]`` and cross-checks each + disposition against the manifest's authoritative ``mesh_count``. + """ + errors: list[str] = [] + coverage = report.get("target_coverage") if isinstance(report, dict) else None + if not isinstance(coverage, dict): + return ["target_coverage missing or not an object"] + entries = coverage.get("entries", []) + by_path: dict[str, dict[str, Any]] = {} + for entry in entries: + if isinstance(entry, dict) and isinstance(entry.get("path"), str): + by_path[entry["path"]] = entry + + for entry in entries: + path = entry.get("path", "") + disposition = entry.get("disposition") + mesh_count = entry.get("mesh_count") + if disposition == "skipped_zero_meshes" and mesh_count != 0: + errors.append( + f"target_coverage entry {path}: skipped_zero_meshes requires " + f"mesh_count == 0, got {mesh_count!r} (a non-zero target cannot be skipped)" + ) + + present_restructure_roles = sorted( + {e.get("role") for e in entries} & RESTRUCTURE_ROLES + ) + if present_restructure_roles and not manifests: + errors.append( + "target_coverage has restructure role(s) " + f"{present_restructure_roles} but no source manifest was supplied or recorded; " + "reconciliation is mandatory once a restructure happened. Record " + "target_coverage.source_manifests[] (or pass --manifest) so the covered set is " + "reconciled against the planned phase4_targets[] instead of self-attested." + ) + + all_resolved = all( + e.get("disposition") in PHASE4_RESOLVED_DISPOSITIONS for e in entries + ) + if coverage.get("complete") is not True: + errors.append( + "target_coverage.complete must be true for a final report " + "(false => a Phase-4 target is unresolved/blocked)" + ) + elif not all_resolved: + errors.append( + "target_coverage.complete is true but some entries are unresolved " + "(only optimized | skipped_zero_meshes | skipped_user_declined count as resolved)" + ) + + if manifests: + planned = _manifest_targets(manifests) + planned_paths = set(planned) + covered_paths = set(by_path) + for path in sorted(planned_paths - covered_paths): + errors.append( + f"target_coverage is missing an entry for manifest phase4_target: {path} " + "(every planned Phase-4 target, across all iterations, must be covered)" + ) + for path in sorted(covered_paths - planned_paths): + errors.append( + f"target_coverage entry {path} is not present in any supplied manifest " + "phase4_targets[] (unexpected target or a missing manifest)" + ) + for path in sorted(planned_paths & covered_paths): + authoritative = planned[path] + disposition = by_path[path].get("disposition") + if ( + disposition == "skipped_zero_meshes" + and isinstance(authoritative, int) + and authoritative > 0 + ): + errors.append( + f"target_coverage entry {path}: skipped_zero_meshes but the manifest's " + f"authoritative mesh_count is {authoritative} > 0 (lying skip)" + ) + return errors + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("report", type=Path, help="Path to the report JSON to validate.") + parser.add_argument("--schema", type=Path, default=DEFAULT_SCHEMA) + parser.add_argument( + "--manifest", + type=Path, + action="append", + default=[], + help="apply-restructure manifest(s) to reconcile Phase-4 coverage against; " + "repeat once per iteration so the union is checked. Manifests recorded in " + "the report's target_coverage.source_manifests[] are loaded automatically " + "and merged with these.", + ) + args = parser.parse_args() + + report = json.loads(args.report.read_text(encoding="utf-8")) + schema = json.loads(args.schema.read_text(encoding="utf-8")) + errors = validate_report(report, schema) + + labeled: list[tuple[str, Any]] = [] + for manifest_path in args.manifest: + labeled.append((manifest_path.name, json.loads(manifest_path.read_text(encoding="utf-8")))) + recorded, load_errors = load_recorded_manifests(report, args.report.resolve().parent) + errors.extend(load_errors) + labeled.extend(recorded) + + for label, manifest in labeled: + errors.extend(f"{label}: {msg}" for msg in validate_manifest_structure(manifest)) + + manifests = [manifest for _, manifest in labeled] + errors.extend(reconcile_target_coverage(report, manifests)) + + if errors: + print(f"{args.report}: INVALID ({len(errors)} error(s))") + for error in errors: + print(f" {error}") + return 1 + print(f"{args.report}: OK") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/skills/omniverse-usd-performance-tuning/references/output-workspace.md b/skills/omniverse-usd-performance-tuning/references/output-workspace.md index c88815aa..425d920c 100644 --- a/skills/omniverse-usd-performance-tuning/references/output-workspace.md +++ b/skills/omniverse-usd-performance-tuning/references/output-workspace.md @@ -50,11 +50,11 @@ probe. ```text ─── Runtime context ─────────────────────────────────────────────────────── -Kit application: {kit.chosen.application} {kit.chosen.version} - path: {kit.chosen.path} - build: {kit.chosen.build} -Scene Optimizer: {sceneOptimizer.extension} {sceneOptimizer.version} -Asset Validator: {assetValidator.package} {assetValidator.version} via {assetValidator.source} +Kit application: {runtime_context.kit.application} {runtime_context.kit.version} + path: {runtime_context.kit.path} + build: {runtime_context.kit.build} +Scene Optimizer: {runtime_context.sceneOptimizer.extension} {runtime_context.sceneOptimizer.version} +Asset Validator: {runtime_context.assetValidator.package} {runtime_context.assetValidator.version} via {runtime_context.assetValidator.source} ─────────────────────────────────────────────────────────────────────────── ``` diff --git a/skills/omniverse-usd-performance-tuning/references/profile-stage/README.md b/skills/omniverse-usd-performance-tuning/references/profile-stage/README.md index 5690ddee..fada6985 100644 --- a/skills/omniverse-usd-performance-tuning/references/profile-stage/README.md +++ b/skills/omniverse-usd-performance-tuning/references/profile-stage/README.md @@ -223,7 +223,7 @@ quick mode plus: ### Prerequisites - Isaac Sim or Kit SDK with RTX renderer. -- Scene Optimizer `omni.kit.profiler.tracy` extension. +- Kit `omni.kit.profiler.tracy` profiler extension (Tracy is a Kit profiler, not a Scene Optimizer component). - GPU with display (headless with virtual display works). ### Usage @@ -346,7 +346,7 @@ When this reference ran in quick mode only, **the report's `verdict` should explicitly note that render-time claims (FPS, frame time, VRAM, draw-call count) are unmeasured**. Improvements predicted by `rtxMeshCount` or prototype sharing are plausible but not verified. See -`skills/omniverse-usd-performance-tuning/references/optimization-report/references/optimization-report-template.md` §"Degraded path (SO +`skills/omniverse-usd-performance-tuning/references/optimization-report/references/optimization-report-template.md` §"Structural-only path (SO unavailable)" and §"Quick-mode-only caveat" for the report wording. ## Rules diff --git a/skills/omniverse-usd-performance-tuning/references/report-templates/optimization-report.design-fixture.json b/skills/omniverse-usd-performance-tuning/references/report-templates/optimization-report.design-fixture.json index 103da941..449580f2 100644 --- a/skills/omniverse-usd-performance-tuning/references/report-templates/optimization-report.design-fixture.json +++ b/skills/omniverse-usd-performance-tuning/references/report-templates/optimization-report.design-fixture.json @@ -166,5 +166,26 @@ "issues": 0, "notes": "No mesh-quality issues found in the sampled path." } - ] + ], + "target_coverage": { + "complete": true, + "source_manifests": ["optimization-report.design-fixture.manifest.json"], + "entries": [ + { + "path": "/tmp/factory/prototypes/rack_unit.usd", + "role": "prototype", + "mesh_count": 412, + "disposition": "optimized", + "operations": ["meshCleanup", "fitPrimitives", "deduplicateGeometry", "decimateMeshes", "optimizeMaterials", "computeExtents"] + }, + { + "path": "/tmp/factory/factory.optimized.usdc", + "role": "assembly_root", + "mesh_count": 1840, + "disposition": "optimized", + "operations": ["meshCleanup", "fitPrimitives", "deduplicateGeometry", "decimateMeshes", "optimizeMaterials", "computeExtents"], + "notes": "Assembly-root remainder (meshes left after extraction) processed through the per-target op chain, not left to stage-level cleanup." + } + ] + } } diff --git a/skills/omniverse-usd-performance-tuning/references/report-templates/optimization-report.design-fixture.manifest.json b/skills/omniverse-usd-performance-tuning/references/report-templates/optimization-report.design-fixture.manifest.json new file mode 100644 index 00000000..9bf0604f --- /dev/null +++ b/skills/omniverse-usd-performance-tuning/references/report-templates/optimization-report.design-fixture.manifest.json @@ -0,0 +1,22 @@ +{ + "mode": "restructure", + "input_stage": "/tmp/factory.usd", + "output_dir": "/tmp/factory", + "new_assembly_root": "/tmp/factory/factory.optimized.usdc", + "phase4_targets": [ + { + "path": "/tmp/factory/prototypes/rack_unit.usd", + "target_class": "prototype", + "mesh_count": 412, + "dependency_group": "shared_first", + "source": "/World/Racks/Rack_01" + }, + { + "path": "/tmp/factory/factory.optimized.usdc", + "target_class": "assembly_root", + "mesh_count": 1840, + "dependency_group": "dependent_after", + "source": "/World" + } + ] +} diff --git a/skills/omniverse-usd-performance-tuning/references/report-templates/optimization-report.md.template b/skills/omniverse-usd-performance-tuning/references/report-templates/optimization-report.md.template index 4fbecadf..133155d9 100644 --- a/skills/omniverse-usd-performance-tuning/references/report-templates/optimization-report.md.template +++ b/skills/omniverse-usd-performance-tuning/references/report-templates/optimization-report.md.template @@ -14,6 +14,18 @@ {{ reasoning }} +{% if runtime_context %} +## Runtime Context + +| Component | Value | +|---|---| +| Kit application | {{ runtime_context.kit.application }} {{ runtime_context.kit.version }} | +| Kit path | {{ runtime_context.kit.path }} | +| Kit build | {{ runtime_context.kit.build }} | +| Scene Optimizer | {{ runtime_context.sceneOptimizer.extension }} {{ runtime_context.sceneOptimizer.version }} | +| Asset Validator | {{ runtime_context.assetValidator.package }} {{ runtime_context.assetValidator.version }} (via {{ runtime_context.assetValidator.source }}) | +{% endif %} + ## Stage Impact Areas | Area | Score | Status | Notes | diff --git a/skills/omniverse-usd-performance-tuning/references/report-templates/optimization-report.structural-only-fixture.json b/skills/omniverse-usd-performance-tuning/references/report-templates/optimization-report.structural-only-fixture.json new file mode 100644 index 00000000..ba39d5f0 --- /dev/null +++ b/skills/omniverse-usd-performance-tuning/references/report-templates/optimization-report.structural-only-fixture.json @@ -0,0 +1,75 @@ +{ + "asset_name": "factory_main", + "input_path": "/data/factory_main.usd", + "output_path": null, + "timestamp": "2026-05-28T00:00:00Z", + "verdict": "neutral", + "workflow_mode": "structural_only", + "notes": "Scene Optimizer was unavailable in the selected runtime and the user declined install, so the workflow ran in structural-only mode: structure assessment plus pre-mutation USD validation only. No mesh operations executed and no optimized stage was written, so the verdict is neutral. FPS, frame time, and VRAM are unmeasured; re-run Phase 1a/6a in full mode under Kit / USD Composer / Isaac Sim to graduate the verdict.", + "optimization_score": 5.0, + "score_scope": "stage_optimization", + "score_label": "neutral", + "reasoning": "Scene Optimizer could not be loaded, so no mutation was attempted. Structure assessment characterized the stage and pre-mutation USD validation ran, but without SO there is no optimized output to compare against the baseline. The score reflects a neutral, no-change result rather than a measured optimization win.\n\nThe report documents the runtime blocker and the next capture needed so a later full-mode run can produce a real before/after verdict.", + "measurement_context": { + "profile_mode": "quick USD composition profile", + "runtime": "standalone USD Python (no Scene Optimizer)", + "score_scope": "stage/composition metrics only" + }, + "runtime_profiling": { + "status": "not_run", + "recommended_tool": "Omniperf", + "dashboard_url": null, + "artifact_path": null, + "summary": "Runtime profiling was not run for this report.", + "caveat": "Use Omniperf for RAM, VRAM, FPS, frame time, shader, renderer, and GPU metrics." + }, + "artifacts": { + "json": "/out/factory_main_optimization_report.json", + "markdown": "/out/factory_main_optimization_report.md", + "html": "/out/factory_main_optimization_report.html" + }, + "metric_groups": [ + { + "id": "composition", + "display_name": "Composition Complexity", + "score": 5.0, + "status": "measured", + "weight": 50, + "summary": "Composition characterized; no mutation applied in structural-only mode." + }, + { + "id": "validation", + "display_name": "Validation", + "score": 5.0, + "status": "measured", + "weight": 50, + "summary": "Pre-mutation USD validation ran; SO performance rules skipped (SO unavailable)." + } + ], + "metrics": [ + { + "name": "prim_count", + "display_name": "Prim Count", + "category": "composition", + "unit": "prims", + "direction": "lower_is_better", + "evidence_type": "direct", + "before": 184213, + "after": 184213, + "change_pct": 0.0, + "verdict": "neutral" + } + ], + "operations": [], + "validators": [ + { + "name": "MinimumOpenability", + "issues": 0, + "notes": "Stage opens; default prim, up-axis, and metersPerUnit present." + } + ], + "target_coverage": { + "complete": true, + "entries": [] + } +} diff --git a/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/README.md b/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/README.md index 403405a8..57526138 100644 --- a/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/README.md +++ b/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/README.md @@ -78,9 +78,10 @@ validation workflows. The SO package includes `omni.scene.optimizer.validators` with `@register_rule` decorators that auto-register 25 SO performance validators into OAV when both packages share the same Python 3.12 environment. No manual `register_all()` call is needed -for rule discovery — just ensure both are importable. Category-scoped runs may -still use `ValidationEngine(init_rules=False)` plus `enable_rule()` for the -selected registered rule classes. +for rule discovery — just ensure both are importable. Selected runs go through +`usd-validation-runner/scripts/usd_validation_executor.py`, which uses +`ValidationEngine(init_rules=False)` plus `enable_rule()` after resolving each +scope-note **canonical concept** to a rule class by identity. > Standalone achieves the same validator coverage as Kit: install > `omniverse-asset-validator` via pip into the same venv where the SO package @@ -139,7 +140,7 @@ they are the preferred runtime (lighter, no Kit overhead, deterministic). Follow `references/standalone-runtime.md` for discovery and verification. If standalone packages are found and importable, set -`runtime_path: "standalone"` in `/setup-preflight.json` and +`runtime_route: "standalone"` in `/setup-preflight.json` and continue to Step 1.6. If standalone packages are not found, fall through to Step 1.5 (Kit discovery). @@ -150,24 +151,25 @@ If standalone is unavailable, look for Kit installations. Follow `references/kit-discovery.md` for discovery order, path classification, auto-enumeration, and candidate records. -Always ask before broad filesystem scanning. If one Kit candidate exists, set -`kit.chosen` to it and continue. If multiple candidates exist, ask the user to -choose; never silently pick one in an interactive session. The newest candidate -is pre-selected. +Always ask before broad filesystem scanning. If one Kit candidate exists, write +it to `runtime_context.kit` and continue. If multiple candidates exist, ask the +user to choose; never silently pick one in an interactive session. The newest +candidate is pre-selected. -Record the chosen candidate and `kit.chosen_by` as described in +Record the chosen candidate and `runtime_context.kit.chosen_by` as described in `references/kit-discovery.md`. ## Step 1.6 - Probe the chosen Kit for SO and AV versions -Once `kit.chosen` is set (or standalone is chosen), run the Python probe from -the chosen launcher and write the probe result to +Once `runtime_context.kit` is set (or standalone is chosen), run the Python +probe from the chosen launcher and write the probe result to `/setup-preflight.json`. Follow `references/runtime-probe.md` for the launcher, import-mode, version-source, and `operationsAvailable` contract. The `runtime_context` object is the literal input to the header template in `references/runtime-context-header.md`. Downstream skills read from this object, -not from the source `kit` / `sceneOptimizer` / `assetValidator` fields. +not from the raw probe `kit` / `sceneOptimizer` / `assetValidator` source +fields. Downstream skills (`so-run-operations`, `omniverse-usd-performance-tuning`, every `so-interpret-validators` recommendation) cross-check `operationsAvailable` @@ -188,6 +190,27 @@ When status is `needs-runtime-choice`, ask exactly for one of these paths: Do not continue to `so-run-validators`, `so-run-operations`, or deep validation until this choice is resolved. +## Non-interactive (batch / CI) mode + +The "stop and ask" behaviors above — the `output_path` prompt, the multiple-Kit +chooser, and the `needs-runtime-choice` gate — assume an interactive session. +For unattended batch or CI runs the caller can pre-supply those inputs, and the +agent must then proceed without blocking: + +- If `output_path`, a runtime preference, and any required candidate paths are + all supplied, do not prompt. +- When the preference is `auto`, resolve the runtime by deterministic policy: + 1. Standalone Scene Optimizer + Asset Validator, if importable. + 2. A user-supplied Kit / USD Composer / Isaac Sim path. + 3. The newest auto-discovered Kit — only when a broad filesystem scan was + explicitly authorized for this run. +- Record `runtime_context.kit.chosen_by: auto_policy` (or + `standalone_preferred`) in `setup-preflight.json` so downstream skills and the + report can show the runtime was selected unattended rather than confirmed by a + human. +- If no runtime resolves under this policy, stop with `needs-runtime-choice` and + name the missing inputs — do not guess a runtime or scan without permission. + ## Step 3 - Verify standalone path If standalone is chosen (Step 1 succeeded), verify each standalone requirement @@ -235,8 +258,8 @@ The header has two formats: - **Format B (compact one-liner)** — used for routine status messages and follow-up prompts once the user has already seen Format A in the session. -When `kit.chosen` is set (single candidate or user has picked), print Format A -once as the conclusion of this reference's interaction with the user, before the +When `runtime_context.kit` is set (single candidate or user has picked), print +Format A once as the conclusion of this reference's interaction with the user, before the agent hands off to `omniverse-usd-performance-tuning`. The user must see exactly which Kit application, Scene Optimizer, and Asset Validator version will be in effect for the rest of the session. diff --git a/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/install-asset-validator-standalone/README.md b/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/install-asset-validator-standalone/README.md index 93429e50..45e3d8a4 100644 --- a/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/install-asset-validator-standalone/README.md +++ b/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/install-asset-validator-standalone/README.md @@ -89,9 +89,10 @@ print(f'Total rules: {len(list(registry.rules))}') Expected: `Usd:Performance` and `Omni:Geometry` categories appear with ~25 additional rules. No `register_all()` call is needed for rule discovery: the -`@register_rule` decorator on each checker class handles registration at import -time. Category-scoped runs may still use `ValidationEngine(init_rules=False)` -plus `enable_rule()` for selected registered rule classes. +validator registration decorators handle registration at import time. Category +names confirm discovery only; `usd-validation-runner` selects validators by +canonical concept and resolves them to rule classes by identity (via +`scripts/usd_validation_executor.py`) before calling `enable_rule()`. ## Output diff --git a/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/install-so-standalone/README.md b/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/install-so-standalone/README.md index 9c3e5984..1f22efa0 100644 --- a/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/install-so-standalone/README.md +++ b/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/install-so-standalone/README.md @@ -33,6 +33,10 @@ project-managed `omniverse-asset-validator` environment that can import the same SO package. Kit remains useful when automatic extension registration or render-time profiling is needed. +This install reference does not define operation invocation. Keep operation +execution examples in `so-run-operations/references/invocation.md` so agents +have one source of truth. + ## Prerequisites > **Python 3.12 is a HARD requirement.** The drop ships `cp312`-only wheels. @@ -144,31 +148,29 @@ process, so the uv-managed-Python caveat above does not apply. python3.12 - <<'PY' def operation_count(): try: - from omni.scene.optimizer.core.bindings._omni_scene_optimizer_core import acquire_interface + from omni.scene.optimizer.core import SceneOptimizerCore - iface = acquire_interface() - if hasattr(iface, "get_operations"): - return "bindings.acquire_interface", len(iface.get_operations()) + return "SceneOptimizerCore.getInstance", len(SceneOptimizerCore.getInstance().getOperations()) except Exception: pass - import omni.scene.optimizer.core as soc + from omni.scene.optimizer.core.bindings._omni_scene_optimizer_core import acquire_interface - core = soc.SceneOptimizerCore.getInstance() - return "SceneOptimizerCore.getInstance", len(core.getOperations()) + iface = acquire_interface() + if hasattr(iface, "get_operations"): + return "bindings.acquire_interface", len(iface.get_operations()) + parser = iface.json_parser() + return "bindings.json_parser", len(parser.get_supported_operations()) surface, count = operation_count() print(f"{surface}: {count} operations") PY ``` -Expect >= 40 (the exact count varies by build). Standalone drops do not all -expose the same Python API: SO 110.0.2-style environments may require -`acquire_interface().get_operations()` / -`acquire_interface().execute_operation(op, context, json_string)`, while other -builds expose `SceneOptimizerCore.getInstance().getOperations()` / -`executeOperation`. Operation scripts must probe the selected runtime before -choosing an API surface. +Expect >= 40 (the exact count varies by build). This verifies import and +operation registry only. Operation invocation is defined by +`so-run-operations/references/invocation.md`; do not infer mutation call shapes +from this install probe. ## Limitations @@ -195,32 +197,30 @@ registry = CategoryRuleRegistry() # Now includes "Usd:Performance" and "Omni:Geometry" categories ``` -No `register_all()` call is needed for rule discovery. The -`@register_rule("Usd:Performance")` decorator on each checker class handles -registration at import time. +No `register_all()` call is needed for rule discovery. The rule registration +decorators handle registration at import time. Do not treat category names as +validation scope, and do not select rules by bare name — the canonical executor +resolves a scope note's concepts to rule classes by identity (a bare +`find_rule()` can't tell the Scene Optimizer and Asset Validator rules that +share a class name apart). -To run only SO performance rules (excluding base OAV rules): +To verify the install can run a scoped concept after `usd-validation-runner` +has scoped the plan: ```python -import omni.scene.optimizer.validators # auto-registers -from omni.asset_validator import ValidationEngine, CategoryRuleRegistry -from pxr import Usd - -registry = CategoryRuleRegistry() -so_rules = [ - rule - for category in ("Usd:Performance", "Omni:Geometry") - for rule in registry.get_rules(category) -] - -engine = ValidationEngine(init_rules=False) -for rule in so_rules: - engine.enable_rule(rule) - -stage = Usd.Stage.Open("path/to/asset.usd") -results = engine.validate(stage) +from usd_validation_executor import load_registry, validate_concepts + +registry = load_registry() +issues = validate_concepts( + "path/to/asset.usd", + ["primvar_indexability"], # canonical concept from the scope note + registry=registry, +) ``` +The executor builds the engine with `init_rules=False` and enables only the +resolved rule class. + The standalone import is `from omni.asset_validator import ValidationEngine` (no `.core`). The `.core` submodule only exists inside a running Kit session. diff --git a/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/install-so-via-kit/README.md b/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/install-so-via-kit/README.md index 28609cf0..464e6d07 100644 --- a/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/install-so-via-kit/README.md +++ b/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/install-so-via-kit/README.md @@ -51,8 +51,8 @@ from omni.kit_app import KitApp import sys app = KitApp() app.startup(['--no-window', '--enable', 'omni.scene.optimizer.core']) -import omni.scene.optimizer.core as soc -print(len(soc.acquire_interface().get_operations())) +from omni.scene.optimizer.core import SceneOptimizerCore +print(len(SceneOptimizerCore.getInstance().getOperations())) sys.exit(app.shutdown()) " ``` @@ -61,10 +61,9 @@ Expect ≥ 40 (floor — varies by version). First run pulls SO from the registry (~minutes); subsequent runs are cached under `~/.local/share/ov/data/Kit/`. -The in-Kit verification path uses `soc.acquire_interface().get_operations()`. -Different standalone drops can expose either this lower-level interface or the -`SceneOptimizerCore` wrapper. Operation scripts should probe the selected -runtime and use the API surface it actually provides. +The in-Kit verification path uses the public `SceneOptimizerCore` registry. +Operation invocation is defined by `so-run-operations/references/invocation.md`; +do not infer mutation call shapes from this install probe. ## Remote Omniverse assets diff --git a/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/kit-discovery.md b/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/kit-discovery.md index aaeb2aae..54a70c77 100644 --- a/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/kit-discovery.md +++ b/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/kit-discovery.md @@ -82,11 +82,17 @@ does not encode them. Sort candidates by semantic version descending. ## User Selection -If one candidate exists, set `kit.chosen` to it and continue. +The enumerated `kit.candidates[]` are the raw discovery source. The selected +candidate becomes the canonical runtime: copy its `application`, `version`, +`path`, and `build` into the `runtime_context.kit` object (the block the header +prints and downstream skills consume). Do not keep a separate `kit.chosen` +copy — `runtime_context.kit` is the single source of truth. + +If one candidate exists, write it to `runtime_context.kit` and continue. If multiple candidates exist, always ask. Pre-select the newest candidate, add `Use standalone libraries instead` as the final option, and record: -- `kit.chosen_by: "user"` for interactive selection. -- `kit.chosen_by: "unattended_default"` when no user input channel exists and - the newest candidate is automatically selected. +- `runtime_context.kit.chosen_by: "user"` for interactive selection. +- `runtime_context.kit.chosen_by: "unattended_default"` when no user input + channel exists and the newest candidate is automatically selected. diff --git a/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/runtime-context-header.md b/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/runtime-context-header.md index 108f7564..58392d69 100644 --- a/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/runtime-context-header.md +++ b/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/runtime-context-header.md @@ -89,11 +89,11 @@ The gate's steps: ``` ─── Runtime context ─────────────────────────────────────────────────────── - Kit application: {kit.chosen.application} {kit.chosen.version} - path: {kit.chosen.path} - build: {kit.chosen.build} - Scene Optimizer: {sceneOptimizer.extension} {sceneOptimizer.version} - Asset Validator: {assetValidator.package} {assetValidator.version} via {assetValidator.source} + Kit application: {runtime_context.kit.application} {runtime_context.kit.version} + path: {runtime_context.kit.path} + build: {runtime_context.kit.build} + Scene Optimizer: {runtime_context.sceneOptimizer.extension} {runtime_context.sceneOptimizer.version} + Asset Validator: {runtime_context.assetValidator.package} {runtime_context.assetValidator.version} via {runtime_context.assetValidator.source} ─────────────────────────────────────────────────────────────────────────── This runtime will be used for the work that follows. Continue, or change it? @@ -128,17 +128,17 @@ run the gate instead. ## Source of truth -Both formats below read from `/setup-preflight.json` (canonical filename + location; see *Where artifacts live* above). The fields the header consumes are: +Both formats below read from the **`runtime_context`** object in `/setup-preflight.json` (canonical filename + location; see *Where artifacts live* above). `runtime_context` is the canonical block the probe writes and downstream skills consume; the header never reads the raw probe `kit` / `sceneOptimizer` / `assetValidator` source fields directly. The fields the header consumes are: -- `kit.chosen.application` — friendly name (e.g. `USD Composer`, `Isaac Sim`, `Kit SDK`) -- `kit.chosen.version` — release version (e.g. `110.1.0`) -- `kit.chosen.path` — absolute install path -- `kit.chosen.build` — full build identifier when present (e.g. `110.1.0+main.10181.f4b28ef2.gl.windows-x86_64.release`) -- `sceneOptimizer.extension` — extension name (e.g. `omni.scene.optimizer.core`) -- `sceneOptimizer.version` — extension version -- `assetValidator.package` — package or extension name -- `assetValidator.version` — version -- `assetValidator.source` — `kit-extension` or `pip` (informs the user whether AV runs through Kit or as a standalone Python install) +- `runtime_context.kit.application` — friendly name (e.g. `USD Composer`, `Isaac Sim`, `Kit SDK`) +- `runtime_context.kit.version` — release version (e.g. `110.1.0`) +- `runtime_context.kit.path` — absolute install path +- `runtime_context.kit.build` — full build identifier when present (e.g. `110.1.0+main.10181.f4b28ef2.gl.windows-x86_64.release`) +- `runtime_context.sceneOptimizer.extension` — extension name (e.g. `omni.scene.optimizer.core`) +- `runtime_context.sceneOptimizer.version` — extension version +- `runtime_context.assetValidator.package` — package or extension name +- `runtime_context.assetValidator.version` — version +- `runtime_context.assetValidator.source` — `kit-extension`, `pip`, or `standalone` (informs the user whether AV runs through Kit or as a standalone Python install) If `/setup-preflight.json` is unavailable when an agent reaches a prompt that requires the header, it must invoke `setup-usd-performance-tuning` first. The header must never be skipped or partially filled. @@ -153,11 +153,11 @@ Use at every decision point where the user is authorizing something that mutates ``` ─── Runtime context ─────────────────────────────────────────────────────── -Kit application: {kit.chosen.application} {kit.chosen.version} - path: {kit.chosen.path} - build: {kit.chosen.build} -Scene Optimizer: {sceneOptimizer.extension} {sceneOptimizer.version} -Asset Validator: {assetValidator.package} {assetValidator.version} via {assetValidator.source} +Kit application: {runtime_context.kit.application} {runtime_context.kit.version} + path: {runtime_context.kit.path} + build: {runtime_context.kit.build} +Scene Optimizer: {runtime_context.sceneOptimizer.extension} {runtime_context.sceneOptimizer.version} +Asset Validator: {runtime_context.assetValidator.package} {runtime_context.assetValidator.version} via {runtime_context.assetValidator.source} ─────────────────────────────────────────────────────────────────────────── ``` @@ -167,8 +167,10 @@ If the user has more than one Kit installed and the workflow has not yet committ Use for routine status messages, ack messages, and follow-up prompts in the same session where the user has already seen Format A. +This file is the **single source of truth** for the Format B string. Any skill that prints it (`omniverse-usd-performance-tuning` initial ack, `compare-profiles` verdict header) must reproduce it character-for-character: + ``` -[Kit: {kit.chosen.application} {kit.chosen.version} | SO: {sceneOptimizer.version} | AV: {assetValidator.version}] +[Kit: {runtime_context.kit.application} {runtime_context.kit.version} | SO: {runtime_context.sceneOptimizer.version} | AV: {runtime_context.assetValidator.version}] ``` Required at: diff --git a/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/runtime-probe.md b/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/runtime-probe.md index 563ba71e..f064f87e 100644 --- a/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/runtime-probe.md +++ b/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/runtime-probe.md @@ -88,12 +88,12 @@ Prefer these sources in order: For supported SO operation keys, use this fallback chain: ```python -# Preferred (standalone prebuilt with impl.core): -from omni.scene.optimizer.impl.core import SceneOptimizerCore +# Preferred: +from omni.scene.optimizer.core import SceneOptimizerCore inst = SceneOptimizerCore.getInstance() ops = inst.getOperations() # returns iterable of operation names -# Fallback (Kit and some standalone builds with bindings): +# Fallback for lower-level binding-only builds: omni.scene.optimizer.core.bindings._omni_scene_optimizer_core \ .acquire_interface().json_parser().get_supported_operations() ``` diff --git a/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/scripts/probe-snapshot.schema.json b/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/scripts/probe-snapshot.schema.json index a032babf..67a5e314 100644 --- a/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/scripts/probe-snapshot.schema.json +++ b/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/scripts/probe-snapshot.schema.json @@ -23,7 +23,7 @@ "type": "array", "items": { "type": "string" }, "uniqueItems": true, - "description": "Operation keys the SO extension registers, as returned by acquire_interface().get_operations(). Sorted alphabetically." + "description": "Operation keys the SO runtime registers, as returned by the selected operation registry probe. Sorted alphabetically." }, "probed_at": { "type": "string", diff --git a/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/scripts/setup-preflight.schema.json b/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/scripts/setup-preflight.schema.json index 0b4680c5..2770872c 100644 --- a/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/scripts/setup-preflight.schema.json +++ b/skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/scripts/setup-preflight.schema.json @@ -76,8 +76,65 @@ }, "runtime_context": { "type": "object", - "description": "Runtime context block that downstream skills (optimization-report) consume. Structure mirrors the optimization-report schema's runtime_context definition.", - "additionalProperties": true + "description": "Canonical runtime context block that downstream skills (optimization-report, the runtime-context-header) consume. The probe writes the chosen runtime here and the header prints from it; the source kit/sceneOptimizer/assetValidator fields above are the raw probe data. Inner shape matches the optimization-report schema's runtime_context definition exactly so the block can be copied verbatim into the report.", + "required": ["kit", "sceneOptimizer", "assetValidator"], + "properties": { + "kit": { + "type": "object", + "required": ["application", "version", "path"], + "properties": { + "application": { + "type": "string", + "description": "Friendly name of the Kit application, e.g. 'USD Composer', 'Isaac Sim', 'Kit SDK'." + }, + "version": { + "type": "string", + "description": "Release version, e.g. '110.1.0'." + }, + "path": { + "type": "string", + "description": "Absolute path to the Kit root." + }, + "build": { + "type": ["string", "null"], + "description": "Full build identifier when present (e.g. '110.1.0+main.10181....release'); null when the install path does not encode one." + } + } + }, + "sceneOptimizer": { + "type": "object", + "required": ["extension", "version"], + "properties": { + "extension": { + "type": "string", + "description": "Extension name, typically 'omni.scene.optimizer.core'." + }, + "version": { + "type": "string", + "description": "Extension version, e.g. '110.0.4'." + } + } + }, + "assetValidator": { + "type": "object", + "required": ["package", "version", "source"], + "properties": { + "package": { + "type": "string", + "description": "Package or extension name, e.g. 'omniverse-asset-validator' or 'omni.asset_validator.core'." + }, + "version": { + "type": "string", + "description": "Package version." + }, + "source": { + "type": "string", + "enum": ["kit-extension", "pip", "standalone"], + "description": "Where Asset Validator was loaded from." + } + } + } + } }, "probed_at": { "type": "string", diff --git a/skills/omniverse-usd-performance-tuning/references/skill-map.md b/skills/omniverse-usd-performance-tuning/references/skill-map.md index 936dbe11..18c8e1d0 100644 --- a/skills/omniverse-usd-performance-tuning/references/skill-map.md +++ b/skills/omniverse-usd-performance-tuning/references/skill-map.md @@ -104,7 +104,7 @@ flowchart TD P4["Phase 4 Per-asset SO ops"] P5["Phase 5 Ref remap + cleanup"] P6["Phase 6 Verify + report"] - P7["Phase 7 Optional iteration"] + P7["Phase 7 Default scoped iteration"] P0 --> P1 --> P2 P2 -->|"already optimized or exit"| P6 P2 -->|"continue"| P3 --> P4 --> P5 --> P6 @@ -116,7 +116,7 @@ flowchart TD - Optimization workflow: `skills/omniverse-usd-performance-tuning/references/workflow.md` - Runtime artifact/token policy: `skills/omniverse-usd-performance-tuning/references/runtime-artifact-token-budget.md` -- Validation routing: `skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/validation-scoping.md` +- Validation routing: `skills/omniverse-usd-performance-tuning/references/usd-validation-runner/README.md` - Validation command references: `skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/` - Scene Optimizer operation mechanics: [`usd-optimize`](https://github.com/NVIDIA-omniverse/usd-optimize/) or the diff --git a/skills/omniverse-usd-performance-tuning/references/so-run-operations/README.md b/skills/omniverse-usd-performance-tuning/references/so-run-operations/README.md index a39c5806..07a94aac 100644 --- a/skills/omniverse-usd-performance-tuning/references/so-run-operations/README.md +++ b/skills/omniverse-usd-performance-tuning/references/so-run-operations/README.md @@ -30,6 +30,8 @@ reads. Do not clone the source repo just to read upstream SO guidance. - Apply local output workspace policy and `runtime-artifact-token-budget.md`; keep logs on disk and read bounded summaries only. - Apply destructive-operation approval gates via `references/operation-safety.md` before mutation. - Keep digitaltwin evidence-to-config routing in `references/config-from-evidence.md`. +- Treat `references/invocation.md` as the only local source of truth for + Python/API invocation shapes. - For Phase 4b multi-target optimization, use `references/batch-mode.md` for target enumeration, adaptive concurrency, prototype-first ordering, hash-based output names, resource observations, and remainder-script prompts. - Preserve logical milestone name `so-run-operations` and hand results to profile/compare/report phases. @@ -45,13 +47,14 @@ Before executing the op chain, re-read and confirm: - [ ] Per-op `parameter_prerequisites` frontmatter read for each destructive op. - [ ] `references/units-and-tolerances.md` — conversion formula for any tolerance-based op. -- [ ] Upstream `usd-optimize` run-operations guide for invocation mechanics. +- [ ] `references/invocation.md` for local invocation mechanics and upstream + handoff. - [ ] `references/batch-mode.md` for multi-target orchestration. - [ ] `runtime-artifact-token-budget.md` §"Stderr Production Guard" — redirect subprocess stderr, cap at 50 MB, retain head/tail samples. ## Execution Handoff -Use upstream `run-operations` for invocation mechanics, operation source forms, +Use `references/invocation.md` for supported Python/API invocation shapes, optional helper wrappers, selected-runtime API probing, output saving, and generic failure handling. Use this local file only for digitaltwin workflow gating, batch orchestration policy, and reporting policy. diff --git a/skills/omniverse-usd-performance-tuning/references/so-run-operations/references/batch-mode.md b/skills/omniverse-usd-performance-tuning/references/so-run-operations/references/batch-mode.md index 94857af0..add3edf0 100644 --- a/skills/omniverse-usd-performance-tuning/references/so-run-operations/references/batch-mode.md +++ b/skills/omniverse-usd-performance-tuning/references/so-run-operations/references/batch-mode.md @@ -17,7 +17,9 @@ Targets come from: - `apply-restructure` mode=`restructure`: prototype USDs, shared layers, and newly loadable sub-assets recorded in - `/apply-restructure-manifest.json` `phase4_targets[]`. + `/apply-restructure-manifest.json` `phase4_targets[]`, plus any + `target_class: "assembly_root"` entry for mesh data retained in the assembly. + Do not filter the manifest to prototype files only. - Composed stages with no restructure: referenced sub-assets from `usd-structure-assessment` Phase 1.2 `assets.manifest`. - Monolithic-as-is: the original stage (`N=1`). @@ -63,7 +65,10 @@ risk says continuing automatically is unsafe. When targets include prototypes and non-prototype assets, run prototypes first, wait for completion, then run non-prototype assets. Parallelize within each dependency group according to the adaptive concurrency policy. Prototype changes -propagate to instances, so running instance-site work first wastes time. +propagate to instances, so running instance-site work first wastes time. Treat +an `assembly_root` target with retained meshes as a non-prototype mesh target: +run the evidence-selected per-target mesh op chain on it before final +assembled-root cleanup. ## Output Naming diff --git a/skills/omniverse-usd-performance-tuning/references/so-run-operations/references/invocation.md b/skills/omniverse-usd-performance-tuning/references/so-run-operations/references/invocation.md index d547fd8d..771401e3 100644 --- a/skills/omniverse-usd-performance-tuning/references/so-run-operations/references/invocation.md +++ b/skills/omniverse-usd-performance-tuning/references/so-run-operations/references/invocation.md @@ -7,15 +7,16 @@ How to execute Scene Optimizer operations once the runtime is selected and the operation plan is approved. Read `/setup-preflight.json` to determine which runtime and API surface to use. +This is the local source of truth for Scene Optimizer operation invocation. +Other workflow docs should link here instead of repeating Python API snippets. + The two runtimes below are peers — neither is preferred. The user's Phase 0 choice determines which section applies. ## Kit Runtime When `setup-preflight.json` indicates Kit as the selected runtime, bootstrap -Kit first, then use the same Python API as standalone. Kit and standalone -environments both expose `SceneOptimizerCore.getInstance()` with -`executeOperation` or `executeConfig`. +Kit first, then use the same supported Python shapes as standalone. ```python import os @@ -35,29 +36,38 @@ app.startup([ # "--enable", "omni.usd_resolver", ]) -import omni.scene.optimizer.core as soc +from omni.scene.optimizer.core import ExecutionContext, SceneOptimizerCore from pxr import Usd # Open stage stage = Usd.Stage.Open(input_path) -# Get optimizer instance -core = soc.SceneOptimizerCore.getInstance() +# Attach the stage to an ExecutionContext before direct API calls. +context = ExecutionContext() +context.set_stage(stage) +core = SceneOptimizerCore.getInstance() # Verify operations are available ops = core.getOperations() # Execute a single operation -config_json = '{"operation": "meshCleanup", "mergeVertices": true}' -core.executeOperation("meshCleanup", stage, config_json) +success, error, output = core.executeOperation( + "meshCleanup", + context, + {"mergeVertices": True}, +) +if not success: + raise RuntimeError(error) # Or execute a pipeline -pipeline_json = '''[ - {"operation": "meshCleanup", "mergeVertices": true}, +pipeline = [ + {"operation": "meshCleanup", "mergeVertices": True}, {"operation": "optimizeMaterials"}, - {"operation": "pruneLeaves"} -]''' -core.executeConfig(stage, pipeline_json) + {"operation": "pruneLeaves"}, +] +for success, error, output in core.executeConfig(context, pipeline): + if not success: + raise RuntimeError(error) # Export optimized output (never overwrite source) stage.Export(output_path) @@ -70,12 +80,9 @@ sys.exit(app.shutdown()) - Cross-check every operation key against `operationsAvailable` in `setup-preflight.json` before execution. If missing, report `blocked_missing_so_operation`. -- Probe the selected runtime before writing the script. Some builds - expose the C++ binding interface from - `omni.scene.optimizer.core.bindings._omni_scene_optimizer_core` instead - of `SceneOptimizerCore`. Use whichever the runtime provides. +- Probe the selected runtime before writing the script. - Set `OMNI_KIT_ACCEPT_EULA=yes` in the environment before KitApp import. -- For analysis-only operations, set `"analysisMode": 1` in the config JSON. +- For analysis-only operations, set `context.analysisMode = 1`. - Operation keys come from the per-operation page's Parameters table and starting-config JSON. Invalid keys may warn or silently no-op. - First run may spend minutes fetching extensions from the registry; subsequent @@ -103,6 +110,62 @@ by the user. - Write optimized stages and runtime artifacts under the local output workspace chosen by setup. +## Verified Python API Shapes + +Verified against +`scene_optimizer_core_usd_25.11_py_3.12@110.1.0+master.401.324ccecb.gl.manylinux_2_35_x86_64.release`. + +Preferred public JSON API: + +```python +import json +from omni.scene.optimizer.core.scripts import standalone +from pxr import Usd + +stage = Usd.Stage.Open(input_path) +ok = standalone.execute_commands_from_json(stage, json.dumps([ + {"operation": "meshCleanup", "mergeVertices": True}, +])) +if not ok: + raise RuntimeError("Scene Optimizer operation chain failed") +stage.Export(output_path) +``` + +Direct API with per-operation results: + +```python +from omni.scene.optimizer.core import ExecutionContext, SceneOptimizerCore +from pxr import Usd + +stage = Usd.Stage.Open(input_path) +context = ExecutionContext() +context.set_stage(stage) +results = SceneOptimizerCore.getInstance().executeConfig(context, [ + {"operation": "meshCleanup", "mergeVertices": True}, +]) +for success, error, output in results: + if not success: + raise RuntimeError(error) +stage.Export(output_path) +``` + +## Invalid Call Shape + +Do not pass a plain `pxr.Usd.Stage` directly as the second argument to +`SceneOptimizerCore.executeOperation` or `executeConfig`. The binding expects an +`ExecutionContext`; the stage must be attached with `context.set_stage(stage)`. +The bad shape below reproduces the failure seen in Horde testing: + +```python +SceneOptimizerCore.getInstance().executeOperation("printStats", stage, {}) +# AttributeError: 'Stage' object has no attribute '_impl' +``` + +If `_impl` appears in an operation log, stop the operation pass, mark the +attempt as an invalid SO invocation, and rerun through the supported shapes +above. Do not export or report a successful optimized stage from that failed +pass. + ## Save Policy - Export optimized output to a NEW `.usdc` path under `/`. diff --git a/skills/omniverse-usd-performance-tuning/references/so-run-operations/references/so-create-proxy/references/proxy-config-recipes.md b/skills/omniverse-usd-performance-tuning/references/so-run-operations/references/so-create-proxy/references/proxy-config-recipes.md index 198d4a47..4cfd4828 100644 --- a/skills/omniverse-usd-performance-tuning/references/so-run-operations/references/so-create-proxy/references/proxy-config-recipes.md +++ b/skills/omniverse-usd-performance-tuning/references/so-run-operations/references/so-create-proxy/references/proxy-config-recipes.md @@ -6,23 +6,19 @@ This local reference preserves the digitaltwin workflow milestone. Scene Optimizer mechanics for this step are owned by upstream `usd-optimize`. -- Public repository: [https://github.com/NVIDIA-omniverse/usd-optimize/](https://github.com/NVIDIA-omniverse/usd-optimize/) -- Package path: `.agents/skills/create-proxy/references/decimate-mode.md` -- Upstream web URL: [https://github.com/NVIDIA-omniverse/usd-optimize/blob/main/.agents/skills/create-proxy/references/decimate-mode.md](https://github.com/NVIDIA-omniverse/usd-optimize/blob/main/.agents/skills/create-proxy/references/decimate-mode.md) +Proxy config recipes are composed from the per-mode sibling handoffs in this +folder rather than restating the same upstream doc. To avoid a duplicate +upstream-doc reference, this stub points to those siblings instead of +re-declaring the package path: -Resolve the upstream guide without cloning the source repo: +- Decimate-based proxy configs: see [`decimate-step-recipes.md`](decimate-step-recipes.md) + (upstream `create-proxy/references/decimate-mode.md`). +- Decimation parameter tuning: see [`decimation-tuning.md`](decimation-tuning.md) + (upstream `create-proxy/references/parameter-tuning.md`). +- Bounding-box proxy configs: see [`bounding-box-proxy-modes.md`](bounding-box-proxy-modes.md) + (upstream `create-proxy/references/bounding-box-modes.md`). -1. `$SCENE_OPTIMIZER_PACKAGE_ROOT/.agents/skills/create-proxy/references/decimate-mode.md` -2. `$SO_HOME/.agents/skills/create-proxy/references/decimate-mode.md` - -If no package root is available, download and extract the published -`scene_optimizer_core_...release.zip` package for the target platform (direct -archive URLs are in `references/upstreams/usd-optimize.md`), or use the package -path/URL supplied by the user. If the user supplies an extracted -package root directly, resolve this same package path under that root. If -GitHub raw fetch is available, the web URL above is acceptable for docs-only -reads. Do not clone the source repo just to read upstream SO guidance. - -For bounding-box configs, use upstream -`.agents/skills/create-proxy/references/bounding-box-modes.md` from the same -package root or public repo. +For the public repository and package-root resolution rules, follow the sibling +handoff above for the relevant mode. Direct archive URLs are in +`references/upstreams/usd-optimize.md`. Do not clone the source repo just to +read upstream SO guidance. diff --git a/skills/omniverse-usd-performance-tuning/references/upstreams/README.md b/skills/omniverse-usd-performance-tuning/references/upstreams/README.md new file mode 100644 index 00000000..6b1244ab --- /dev/null +++ b/skills/omniverse-usd-performance-tuning/references/upstreams/README.md @@ -0,0 +1,23 @@ +# Upstream Source-of-Truth References + + + + +Pointers to the upstream repositories and prebuilt packages this skill delegates +to instead of reimplementing. Operation mechanics, parameters, defaults, and +package resolution live upstream; this skill owns only the digital twin workflow +routing, runtime setup, validation scope, output policy, and reporting that wrap +them. + +When a file here names a tool, prefer the upstream URL it records for the most +current version — the local notes are a snapshot and a resolution recipe, not a +copy of the upstream docs. + +## Contents + +- [`usd-optimize.md`](usd-optimize.md) — Scene Optimizer operation mechanics and + prebuilt-package resolution (upstream + [usd-optimize](https://github.com/NVIDIA-omniverse/usd-optimize/)). Resolve + per-operation guides through `$SCENE_OPTIMIZER_PACKAGE_ROOT` / `$SO_HOME` or + the upstream `.agents/operations/.md` path rather than duplicating them + in this repo. diff --git a/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/README.md b/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/README.md index 7cd37560..cf794558 100644 --- a/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/README.md +++ b/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/README.md @@ -54,11 +54,11 @@ Before producing the SA report, re-read and confirm: ## Limitations -- Phase 1 is purely structural: metadata, composition arcs, prim traversal. +- SA Stage 1 is purely structural: metadata, composition arcs, prim traversal. No geometry arrays (points, faceVertexCounts) are read. No renderer, viewport, or BBoxCache computation. Mesh-level stats (triangle counts, density) are deferred to Phase 2c validators (SO analysis mode). -- Phase 2 heuristics flag validation candidates; they do not justify operations +- SA Stage 2 heuristics flag validation candidates; they do not justify operations by themselves. - Outlier detection (§2.1) uses authored `extentsHint` attributes when present. If extents are not authored, SA cannot flag spatial outliers — Phase 2c @@ -80,7 +80,7 @@ Before running, read: If you have network access, prefer the live URLs (noted in each reference file). -## Phase 1: Structure Analysis (no geometry load) +## SA Stage 1: Structure Analysis (no geometry load) These checks run without a Kit viewport, GPU, or renderer. SA opens the stage with `Usd.Stage.Open(path)` (default load rules) and traverses the composed @@ -151,7 +151,7 @@ Report: - Flag prims with geometry but no kind assignment. - Flag kind assignments that don't match the hierarchy (e.g., a component inside a component). -## Phase 2: Structural Heuristics (metadata only, narrows validation scope) +## SA Stage 2: Structural Heuristics (metadata only, narrows validation scope) These checks use authored extent metadata (`extentsHint`) and structural patterns to identify assets that likely need deep validation. They do not @@ -458,9 +458,9 @@ The `validation_scope` section feeds directly into `so-run-validators` — it te the agent which assets to validate and which to skip. The `summary_counts` section is the compact handoff consumed by -`usd-validation-runner` and `skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/validation-scoping.md`: it tells the -validator router whether the asset is monolithic, prototype-heavy, already -instanced, or worth restructuring before expensive validators run. +`usd-validation-runner`: it tells the validator router whether the asset is +monolithic, prototype-heavy, already instanced, or worth restructuring before +expensive validators run. For `reference_count` and `payload_count`, prefer authored list-op item counts. If the selected USD Python runtime can only produce prim-level booleans, use diff --git a/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/apply-restructure/README.md b/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/apply-restructure/README.md index 6a9eb64e..855e03c6 100644 --- a/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/apply-restructure/README.md +++ b/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/apply-restructure/README.md @@ -50,8 +50,8 @@ Before executing restructure writes, re-read and confirm: never overwrites the original stage in-place. - [ ] `setup-preflight.json` runtime context — confirm USD Python environment is available for authoring. -- [ ] After restructure: re-validate (Phase 3a sweep) to confirm no - composition breaks. +- [ ] After restructure: run scoped re-validation to confirm no composition + breaks. ## Limitations @@ -108,7 +108,8 @@ Manifest schema: "phase4_targets": [ { "path": "", - "target_class": "prototype | shared_layer | loadable_subasset", + "target_class": "prototype | shared_layer | loadable_subasset | assembly_root", + "mesh_count": 0, "dependency_group": "shared_first | dependent_after | independent", "source": "", "weight_hints": { @@ -133,6 +134,17 @@ Manifest schema: } ``` +The strict contract for this file is `scripts/apply-restructure-manifest.schema.json`. +Every `phase4_targets[]` entry MUST carry a top-level `mesh_count` (integer >= 0): +the authoritative default-predicate count +(`len([p for p in Usd.PrimRange.Stage(stage, Usd.PrimDefaultPredicate) if p.IsA(UsdGeom.Mesh)])`) +measured with the target opened standalone, matching the Postcondition below. +`weight_hints.mesh_count` remains an optional, non-authoritative batching estimate. +The downstream Phase-4 completion gate (`optimization-report/scripts/validate_report.py +--manifest`) reconciles the final report's `target_coverage` against the UNION of +every iteration's `phase4_targets[]` and accepts a `skipped_zero_meshes` disposition +only when this `mesh_count` is `0`, so a retained-mesh target cannot be silently dropped. + ## Preconditions - `input_stage` opens cleanly (use `validate-usd-minimum` to confirm before starting). @@ -161,7 +173,9 @@ Manifest schema: - For `mode=restructure`: the manifest documents what mesh content remains on the assembly root after extraction. If the assembly root has > 0 mesh prims, include it in `phase4_targets[]` with `target_class: "assembly_root"` so - Phase 4 does not skip it. + Phase 4 does not skip it. Downstream Phase 4 must process that entry through + the per-target mesh op chain for its retained meshes; it is not limited to + final stage-level cleanup operations. --- diff --git a/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/apply-restructure/references/restructure-mode.md b/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/apply-restructure/references/restructure-mode.md index ccfb4d70..d32d33cd 100644 --- a/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/apply-restructure/references/restructure-mode.md +++ b/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/apply-restructure/references/restructure-mode.md @@ -3,8 +3,9 @@ # Restructure Mode -Use this reference for `apply-restructure` mode=`restructure` after -`restructure-decision` returns `proceed`. +Use this reference for `apply-restructure` mode=`restructure`, invoked when +`restructure-decision` selects the `extract-as-assets` or +`decompose-for-selective-loading` branch. ## Internal-Reference Scan @@ -79,7 +80,7 @@ Execution order: a. Create the interface + payload layers following the reference-payload pattern above. b. Set `instanceable=true` on the payload root prim only when - `instancing-readiness` (see `restructure-decision/README.md` + `instancing-readiness` (see `skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/restructure-decision/README.md` §"instancing-readiness gate") passes for that site's dedupe group. c. Reference the site's interface layer from the assembly root. 3. For unique (non-duplicate) boundary candidates, extract as independent diff --git a/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/apply-restructure/scripts/apply-restructure-manifest.schema.json b/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/apply-restructure/scripts/apply-restructure-manifest.schema.json new file mode 100644 index 00000000..6e8d269b --- /dev/null +++ b/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/apply-restructure/scripts/apply-restructure-manifest.schema.json @@ -0,0 +1,84 @@ +{ + "$comment": "SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\nSPDX-License-Identifier: Apache-2.0", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Apply-Restructure Manifest", + "description": "Contract for /apply-restructure-manifest.json, the Phase 2f -> Phase 4 handoff. phase4_targets[] is the authoritative list of files Phase 4 must mesh-optimize; the final optimization-report's target_coverage must cover the UNION of every iteration's phase4_targets (see validate_report.py --manifest). A mode=restructure manifest MUST carry a non-empty phase4_targets[], and every entry MUST declare its default-predicate mesh_count so a 'skipped_zero_meshes' disposition cannot be faked.", + "type": "object", + "required": ["mode", "phase4_targets"], + "properties": { + "mode": { + "type": "string", + "enum": ["restructure", "ref_remap"] + }, + "input_stage": { "type": "string" }, + "output_dir": { "type": "string" }, + "new_assembly_root": { "type": "string" }, + "outputs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { "type": "string" }, + "kind": { + "type": "string", + "enum": ["prototype", "shared_layer", "loadable_subasset", "parent_assembly", "new_root"] + }, + "provenance": { "type": "string" }, + "size_bytes": { "type": "integer", "minimum": 0 }, + "validate_usd_minimum": { "type": "string", "enum": ["pass", "fail", "skipped"] }, + "notes": { "type": "string" } + } + } + }, + "phase4_targets": { + "type": "array", + "description": "Every written prototype/shared layer/loadable sub-asset that needs Phase 4 mesh optimization, PLUS the assembly root itself when it retains > 0 meshes after extraction.", + "items": { + "type": "object", + "required": ["path", "target_class", "mesh_count"], + "properties": { + "path": { + "type": "string", + "description": "Written file Phase 4 must optimize. This is the reconciliation key against optimization-report.target_coverage[].path." + }, + "target_class": { + "type": "string", + "enum": ["prototype", "shared_layer", "loadable_subasset", "assembly_root"] + }, + "mesh_count": { + "type": "integer", + "minimum": 0, + "description": "Authoritative default-predicate mesh count: len of Usd.PrimRange.Stage(stage, Usd.PrimDefaultPredicate) filtered to UsdGeom.Mesh, measured when the target is opened standalone (matches the apply-restructure Postcondition). The Phase-4 completion gate accepts disposition 'skipped_zero_meshes' only when this is 0." + }, + "dependency_group": { + "type": "string", + "enum": ["shared_first", "dependent_after", "independent"] + }, + "source": { "type": "string" }, + "weight_hints": { + "type": "object", + "description": "Optional pre-extraction estimates for adaptive batching. NON-authoritative; mesh_count above is the authoritative count the gate uses." + }, + "notes": { "type": "string" } + } + } + }, + "rewrite_steps": { "type": "array" }, + "material_rewrites": { "type": "array" }, + "warnings": { "type": "array" } + }, + "allOf": [ + { + "if": { + "properties": { "mode": { "const": "restructure" } }, + "required": ["mode"] + }, + "then": { + "properties": { + "phase4_targets": { "minItems": 1 } + }, + "required": ["phase4_targets"] + } + } + ] +} diff --git a/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/composition-audit.md b/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/composition-audit.md index e0c6de2d..cb503722 100644 --- a/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/composition-audit.md +++ b/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/composition-audit.md @@ -3,7 +3,7 @@ # USD Composition Audit -> Composition audit is performed as part of `usd-structure-assessment` Phase 1.1; this reference holds the deeper checklist, findings taxonomy, and output schema mapping. +> Composition audit is performed as part of `usd-structure-assessment` SA Stage 1; this reference holds the deeper checklist, findings taxonomy, and output schema mapping. --- @@ -11,7 +11,7 @@ Audit the composed stage and authored layers before any processor changes USD content, so downstream optimization can choose safe edit targets and understand composition risks. -This is invoked as a section of `usd-structure-assessment` Phase 1 (composition inventory, asset inventory) and consulted from `usd-edit-target-planner` and `restructure-flow` when deeper composition detail is needed. +This is invoked as a section of `usd-structure-assessment` SA Stage 1 (composition inventory, asset inventory) and consulted from `usd-edit-target-planner` and `apply-restructure` when deeper composition detail is needed. ## Schema reconciliation @@ -69,7 +69,7 @@ When in doubt, write the SA umbrella shape - the audit-report subset is recovera - Candidate edit targets. - Payloads or variants requiring separate coverage. - Evidence needed before Scene Optimizer handoff. -- **Referenced asset manifest** - a list of unique asset layer paths that contain geometry or material data via references or payloads. Downstream skills (`usd-edit-target-planner`, `restructure-flow`, Scene Optimizer handoff) need this list to plan per-asset optimization. +- **Referenced asset manifest** - a list of unique asset layer paths that contain geometry or material data via references or payloads. Downstream skills (`usd-edit-target-planner`, `apply-restructure`, Scene Optimizer handoff) need this list to plan per-asset optimization. ## Output diff --git a/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/instancing-readiness/references/instancing-tradeoffs.md b/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/instancing-readiness/references/instancing-tradeoffs.md index 5bb6ea8c..1f229f9a 100644 --- a/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/instancing-readiness/references/instancing-tradeoffs.md +++ b/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/instancing-readiness/references/instancing-tradeoffs.md @@ -15,7 +15,7 @@ This reference is consulted by: - `usd-hierarchy-dedupe-candidates` when choosing between hierarchy dedupe vs unscoped mesh dedupe. - `instancing-readiness` when explaining merge safety to the user before authoring `instanceable=true`. -- `restructure-flow` when planning Phase 2f restructure orchestration. +- `apply-restructure` when planning Phase 2f restructure orchestration. - `so-interpret-validators` when recommending merge or dedupe ops based on validator findings. ## Prerequisites diff --git a/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/restructure-decision/README.md b/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/restructure-decision/README.md index 36c61d7d..9aeabfca 100644 --- a/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/restructure-decision/README.md +++ b/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/restructure-decision/README.md @@ -64,7 +64,7 @@ The agent assembles a decision packet from prior phases: |---|---|---| | SA classification | `usd-structure-assessment` Phase 2a | Monolithic vs composed; restructure recommended? | | Asset-boundary candidates | `usd-structure-assessment` §2.7 + `usd-hierarchy-dedupe-candidates` | Where the cut points are if restructure is chosen | -| Validator findings | Phase 2c `usd-validation-runner` sweep | Whether structural-only fixes would be wasted on a stage about to be restructured | +| Validator findings | Phase 2c `usd-validation-runner` selected probes | Whether structural-only fixes would be wasted on a stage about to be restructured | | Instancing assessment | Phase 2d (read from SA `instancing` field) | Estimated leverage from restructure | | User constraints | session context | Time budget, mutation policy, output policy | @@ -201,7 +201,7 @@ User accepts the existing structure. Skip Phase 2f. Continue to Phase 3 (instanc ### exit -User declines mutation. Skip to Phase 6e and write a diagnosis-only optimization report capturing the SA + validator findings. +User declines mutation. Skip to Phase 6d and write a diagnosis-only optimization report capturing the SA + validator findings. ### jump-to-verify @@ -289,7 +289,7 @@ Record the user's choice in the optimization plan and emit it for downstream pha - If the user picks `decompose-for-selective-loading`, hand off to `apply-restructure` with the selected boundary level and `goal: selective_loading`; do not perform writes from this reference. -- If the user picks `exit`, immediately go to Phase 6e (`optimization-report`) - do not silently continue to Phase 3. +- If the user picks `exit`, immediately go to Phase 6d (`optimization-report`) - do not silently continue to Phase 3. ## Limitations diff --git a/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/scripts/audit-report.schema.json b/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/scripts/audit-report.schema.json index 8ece9182..da95f8b9 100644 --- a/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/scripts/audit-report.schema.json +++ b/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/scripts/audit-report.schema.json @@ -2,7 +2,7 @@ "$comment": "SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\nSPDX-License-Identifier: Apache-2.0", "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "USD Performance Tuning Composition Audit Report", - "description": "Composition sub-shape of the umbrella usd-structure-assessment-report.schema.json. Covers the composition slice only (detailed arrays). The umbrella schema uses counts for routing; this schema provides the full arc lists for tools that need them.", + "description": "Composition sub-shape of the umbrella usd-structure-assessment-report.schema.json. Covers the composition slice only (detailed arrays). The umbrella schema uses counts for routing; this schema provides the full arc lists for tools that need them. Casing note: this standalone sub-shape intentionally keeps camelCase keys (e.g. usedLayers, instanceablePrims, unresolvedAssetPaths, recommendedNextActions) to preserve compatibility with external tools/pipelines that consume the composition slice in isolation. The umbrella SA report is itself mixed-case (snake_case SA-domain fields alongside camelCase USD-native stage keys like rootLayer/upAxis/metersPerUnit); do not rename these keys without coordinating with those standalone consumers.", "type": "object", "required": ["schemaVersion", "stage", "composition", "findings", "recommendedNextActions"], "properties": { diff --git a/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/scripts/usd-structure-assessment-report.schema.json b/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/scripts/usd-structure-assessment-report.schema.json index 762555aa..93eab3a2 100644 --- a/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/scripts/usd-structure-assessment-report.schema.json +++ b/skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/scripts/usd-structure-assessment-report.schema.json @@ -104,7 +104,7 @@ }, "summary_counts": { "type": "object", - "description": "Routing-critical counts consumed by validation-scoping.md decision tree and restructure-decision. Strict: typos rejected.", + "description": "Routing-critical counts consumed by usd-validation-runner policy and restructure-decision. Strict: typos rejected.", "required": [ "prim_count", "mesh_count", diff --git a/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/README.md b/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/README.md index fb4d2138..d5d35645 100644 --- a/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/README.md +++ b/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/README.md @@ -5,277 +5,557 @@ ## When to Use -Use this reference for validation-only requests or when the performance workflow reaches the validation-routing phase. +Use this reference for validation-only requests or when the performance workflow +reaches Phase 2c, Phase 4d, Phase 6b, or an iteration that needs validation +evidence. ## Instructions -1. Identify whether the request is validation-only or a validation phase inside the optimization workflow. -2. Select the smallest validation stack that can change the user-visible decision or operation plan. -3. Ask before full Asset Validator sweeps or Tier 3 expensive cross-component checks. -4. Route execution to the owning validation reference or skill and preserve evidence paths for later reporting. - +1. Identify whether the request is validation-only or a validation phase inside + the optimization workflow. +2. Use structure assessment and profile evidence before selecting validators. + Do not instantiate a validator engine, import Scene Optimizer validators, or + enumerate/run rules until a selected validation plan exists. +3. Select the smallest validation stack that can change the user-visible + decision or operation plan. +4. Ask before any full Asset Validator sweep, Tier 3 expensive probe, or + expanded iteration scope. +5. Route execution to the owning validation reference or skill and preserve + evidence paths for later reporting. + +## Ownership Boundary + +This runner is the single owner for validation scoping, full-sweep approval, +large-stage thresholds, masked-stage spot-check policy, and selected-probe +planning. Downstream validator references such as +`references/validate-usd-asset-validator.md` consume the scope note and own +runtime invocation details only. ## Pre-flight Checklist -Before running validators, re-read and confirm: +Before running validators, confirm: + +- [ ] The workflow has SA `summary_counts`, `phase_recommendation`, + `validation_scope`, and `flagged_assets` unless this is a direct + validation-only request. +- [ ] The stage is classified for validation planning as small or large using + the thresholds below. +- [ ] The plan names selected rules and probes, why they were selected, why a + full sweep was skipped or approved, and artifact paths. +- [ ] Expensive checks and full sweeps have explicit user approval when needed. +- [ ] Findings will be routed to `so-interpret-validators` for op-chain + construction; do not map findings to ops yourself. -- [ ] `references/validation-scoping.md` — tier selection, phase-aware subsets, - deferred validators, full-sweep approval gate. -- [ ] SA report's `phase_recommendation` and `summary_counts` drive tier selection. -- [ ] Apply `runtime-artifact-token-budget.md` for large CSV/log output. -- [ ] Route findings to `so-interpret-validators` for op-chain construction — - do not map findings to ops yourself. ## Output Format -Return a scoped validation plan or validation summary naming the selected validator stack, skipped expensive checks, approval gates, artifact paths, and findings that affect the optimization plan. +Return a scoped validation plan or validation summary naming the selected +validator stack, selected rules and probes, skipped expensive checks, approval +gates, artifact paths, and findings that affect the optimization plan. + +For Phase 2c, also write a compact scope note matching +`scripts/validation-scope-note.schema.json`. Validators are named by **canonical +concept**, not runtime class name: + +```json +{ + "scope": "targeted", + "concepts": ["primvar_indexability", "geom_duplicates"], + "targets": [ + { "concept": "primvar_indexability", "paths": ["/World/Racks/Rack_A"] }, + { "concept": "geom_duplicates", "mask_paths": ["/World/Racks"] } + ], + "tier_assignments": { "primvar_indexability": 2, "geom_duplicates": 3 }, + "selection_reason": "...", + "full_sweep": { "status": "skipped", "reason": "...", "approved_by_user": false }, + "artifact_paths": ["..."] +} +``` + +The scope note is the input contract for `scripts/usd_validation_executor.py`. ## Purpose -Use this reference whenever a workflow needs to surface USD validity or performance -validator issues. It picks the smallest validation stack that can affect the -optimization plan, records the evidence contract, and routes concrete execution -to the owning skill or reference. +Use this reference whenever a workflow needs to surface USD validity or +performance validator issues. It picks the smallest validation stack that can +affect the optimization plan, records the evidence contract, and routes concrete +execution to the owning skill or reference. -This reference **does not** execute optimization operations and **does not** choose fix strategies. +This reference **does not** execute optimization operations and **does not** +choose fix strategies. -Use this reference as the entry point for validation-only requests such as -"validate this USD", "run Asset Validator", or "show validation issues". For -broad performance diagnosis, slow loading, high memory, low FPS, or "what -should I optimize?", start with -`omniverse-usd-performance-tuning` so structure assessment can scope validation -before expensive validator runs. +For broad performance diagnosis, slow loading, high memory, low FPS, or "what +should I optimize?", start with `omniverse-usd-performance-tuning` so structure +assessment can scope validation before expensive validator runs. For `omniverse://` targets, start with `omniverse-authentication` before this skill attempts runtime probing or stage open. -Tier tables and the JSON plan template live in -`references/validation-scoping.md` under this reference. This router points at that -reference for tier detail rather than duplicating it, which keeps validation -policy portable for future SO/OAV repo splits. - ## Prerequisites - Target stage or asset paths and resolver context. -- Available validator runtime (Omni Asset Validator inside Kit, project-managed AV install, or installed Scene Optimizer APIs). +- Available validator runtime (Omni Asset Validator inside Kit, project-managed + AV install, or installed Scene Optimizer APIs). - Artifact directory for logs, CSV/JSON findings, and provider summaries. - Baseline, waiver, or failure policy for pre/post processing gates. -- (For perf-stack scoping) `usd-structure-assessment` report with `phase_recommendation` and `flagged_assets`. +- For performance-stack scoping: `usd-structure-assessment` report with + `summary_counts`, `phase_recommendation`, `validation_scope`, and + `flagged_assets`. -## Session-start runtime gate (mandatory when this is the entry skill) +## Session-start runtime gate If this reference is the **entry skill** for the user's request (i.e., the agent invoked `/usd-validation-runner` directly rather than through `omniverse-usd-performance-tuning`), run the session-start gate from -`skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/runtime-context-header.md` *Mandatory session-start gate* +`skills/omniverse-usd-performance-tuning/references/setup-usd-performance-tuning/references/runtime-context-header.md` before any routing. The gate determines `output_path`, checks `/setup-preflight.json`, invokes `setup-usd-performance-tuning` if the preflight is missing, then surfaces Format A + the 4-option -confirmation. Do not pick a validator stack until the user has -confirmed the runtime. +confirmation. Do not pick a validator stack until the user has confirmed the +runtime. -If invoked **downstream of an entry skill that already fired the gate -in the same session**, skip the gate and proceed. +If invoked downstream of an entry skill that already fired the gate in the same +session, skip the gate and proceed. -## Limitations +--- -- Validation depends on tools installed outside this repo. -- Scene Optimizer validation results are valid only after imports and checker classes are verified in the active runtime. -- Validation evidence is not cleanup; auto-fixes require separate change recording. +## Phase 2c Order: Scope Before Code -## Troubleshooting +Phase 2c is **Phase-aware validation scope + selected probes**. It is not a +default validator sweep. -- If `omni_asset_validate` is unavailable, record it as missing rather than fabricating a pass. -- If Scene Optimizer validator imports fail, do not report SO-specific results. -- If the bundled `validator-venv` is slow or lacks dependencies, prefer a Kit or project-managed Asset Validator environment. +Required order: ---- +1. Read Phase 1 profile and `usd-structure-assessment` output. +2. Classify the asset as small or large for validation planning. +3. Build the selected validation plan from `summary_counts`, + `phase_recommendation`, `validation_scope`, and `flagged_assets`. +4. Record the scope note/artifact. +5. Only then run the selected rules or probes. -## Validator Stack Matrix +For monolithic `optimize-as-is`, the original stage remains the optimization +target, but validation still follows this selected-scope policy. A monolithic +target does not authorize a full sweep. -The router picks one or more stacks based on the intent and the structure assessment `phase_recommendation`. Each stack lists the owned reference that describes the concrete validation command; this reference orchestrates the routing. +## Large for Validation Planning -**All validation cost-control policy lives in -`references/validation-scoping.md`.** That reference owns tier tables, -the full-sweep approval gate, deferred-validator rules, spot-check -policy, phase-aware subset selection, and the per-rule timeout pattern. -Read it when building a perf-stack plan. The key principles: +Treat a stage as **large for validation planning** when any condition is true: -- Structure assessment first — never start with a full default AV sweep. -- Full-sweep approval gate — ask before a default rule set on large/unknown assets. -- SO analysis stays in the validation plan regardless of execution path. -- Spot-check before dropping signal — masked-stage with ≥25% mesh coverage. +- Resolved stage/root package size is unknown or `>100 MB`. +- Composed prim count is `>10,000`. +- Mesh count or prototype/proxy mesh contribution is high enough that a + category sweep would traverse substantial geometry. +- The target is customer-scale CAD/BIM/MEP/factory/plant/city content. +- The request is performance optimization rather than formal conformance. -### Pre-Mutation USD Stack +Large-stage behavior: -For "is this asset USD-correct enough to mutate?", run only the checks needed -to protect the requested mutation. +- Do not run a default full-stage Asset Validator or Scene Optimizer rule sweep. +- Ask before full sweep if the user explicitly wants exhaustive validation. +- Prefer minimum-openability, Tier 1 cheap whole-stage stats/probes, targeted + rules, Tier 2/3 subprocess runs with timeouts, or masked-stage + spot checks. +- Record skipped full-sweep rationale in the scope note/artifact. -| Step | Owner | When | -|---|---|---| -| 1 | Inline minimum-openability check | Always before mutation. Confirm the stage opens, root/default prim is valid enough for the task, asset paths resolve, and scale/up-axis metadata is known or recorded as missing. | -| 2 | `references/validate-usd-asset-validator.md` | When executable NVIDIA Omniverse Asset Validator coverage is needed. Scope by targeted rules/categories unless the user explicitly approves a full sweep. | -| 3 | External profile/package validators | Only when the user explicitly requests a domain profile such as SimReady or a package-validation workflow. Do not keep those command references in this package. | +## Full-Sweep Approval Gate -### Performance stack (scoped) +Trigger before any command or API call that enables the default AV rule set, +all registered rules, all categories, or all SO performance validators over the +whole composed stage when any large-stage condition above holds. -For "is this asset performant?" The chain is: scope -> run -> interpret. Tier and phase-aware scoping live in `references/validation-scoping.md`; the router instructs the agent to load that reference and emit a plan, then invoke the downstream tools. +Ask before full sweep and offer: -| Step | Tool / reference | When | -|---|---|---| -| 1 | (Read `references/validation-scoping.md`) | Build the tier plan from the structure assessment's `flagged_assets` + `phase_recommendation`. | -| 2 | `so-run-validators` | Execute the tier plan against the asset. | -| 3 | `so-interpret-validators` | Read artifacts, classify findings (T1/T2/T3), recommend fixes. | +- **Recommended:** minimum-openability + targeted rule/probe checks. +- **Full sweep:** default rule set with explicit timeout and artifact dir. +- **Defer:** skip full sweep until after mutation or a narrower follow-up. -### Phase-aware subset selection +When approved, record `scope: "approved_full_sweep"`, +`approved_by_user: true`, `timeout_seconds`, and artifact paths. If not +approved, do not launch. -Owned by `references/validation-scoping.md` → *Decision Tree*. Document the -chosen subset in the validation plan so Phase 6 re-validation can reproduce -the same scope. +## Validator Tiers ---- +Tiers describe **execution posture**. Which concept is which tier lives only in +`validator-concepts.json` — do not infer or assert a concept's tier here. -## Routing decision +### Tier 1: Cheap Whole-Stage Stats/Probes -For a given request, pick the stack(s) using this table: +Tier 1 registry concepts plus pure profiling probes that are not concepts +(`printStats`, `countVertices`). Safe to run in one batch over the SA-selected +target; not a default AV all-rules sweep. -| Intent | Stacks | -|---|---| -| "Validate this USD before any mutation." | Pre-mutation USD stack: minimum-openability plus targeted Asset Validator coverage when needed. | -| "What's wrong with this asset?" (broad performance ask) | `usd-structure-assessment` first, then Performance stack scoped per the structure assessment. Add pre-mutation USD stack only when the request is about USD validity or mutation safety. | -| "Run perf validators only." | Performance stack only. | -| "Validate the optimized output (Phase 6 re-validation)." | Same stacks that ran in Phase 2c, for a fair before/after comparison. | -| "Validate a SimReady package/profile." | Defer to the external SimReady/Foundation validation workflow; keep only local USD and performance evidence in this package. | +### Tier 2: Targeted Medium Probes -If intent is ambiguous, ask the user before expanding scope - especially before -invoking Tier 3 perf validators or a default full-stage Asset Validator sweep -(both can be very slow and can produce very large logs/reports). +Tier 2 registry concepts, run per flagged asset (or a bounded sample) in +killable subprocesses. -For performance work, the validation output should be a compact -operation-oriented summary, not a raw issue dump. Use -`so-interpret-validators/references/rule-reference.md` for rule-to-operation -mapping instead of maintaining another mapping in this router. +### Tier 3: Expensive Probes (evidence-gated, mandatory when flagged) ---- +Spatial, pairwise, or high-cardinality analysis. The Tier 3 set is exactly the +concepts `validator-concepts.json` marks `tier: 3` — resolve it from the +registry, do not enumerate it here (see the rule at the top of this section). -## Commands +**Tier 3 is not optional.** When structure assessment flags a target for a +Tier 3 concept, running the **scoped probe is required** — it carries signal the +later op plan depends on, and skipping it is how runs miss real optimizations. +What is approval-gated is *cost*, not *coverage*: -`references/validate-usd-asset-validator.md` owns Asset Validator runtime -details. `so-run-validators` owns Scene Optimizer performance-validator -execution. This router keeps only the common direct invocation shape: +- **Scoped probe = default, no approval needed.** Restrict to the flagged + paths/pairs with `paths=` / `Usd.Stage.OpenMasked()` and run in a bounded + subprocess with a timeout. This is the normal Tier 3 path. +- **Full-stage probe = approval-gated.** Only run the un-scoped, whole-stage + version after the full-sweep approval gate. +- **Timeout is a recorded disposition, not a skip.** If the scoped probe times + out, record `timeout_recorded` and retry a masked/standalone sample — do not + drop the target. -Base Asset Validator (used by `validate-usd-asset-validator`). Probe the -selected runtime before using optional output flags; use JSON output when -the selected runtime advertises it, otherwise fall back to CSV. The commands -below are invocation shapes, not default scope choices. A bare full-stage -invocation is allowed only after the full-sweep gate has been approved. +Every flagged Tier 3 target must end in a coverage-ledger disposition (see +**Completion Gate**). "I skipped it because it was expensive" is not a valid +outcome; the valid outcomes are probed (clean or with findings), `user_declined` +after an explicit ask, `timeout_recorded`, or `blocked_validation_runtime`. -```bash -omni_asset_validate --help -omni_asset_validate /path/to/asset.usd \ - --category Geometry \ - --csv-output /path/to/artifacts/issues.csv -``` - -Minimum openability baseline is a USD-open check, not a default Asset -Validator run: +## Completion Gate (coverage ledger) -```python -from pxr import Usd - -stage = Usd.Stage.Open("/path/to/asset.usd") -if stage is None: - raise RuntimeError("stage did not open") -root = stage.GetDefaultPrim() -print({ - "opened": True, - "default_prim": root.GetPath().pathString if root else None, - "up_axis": stage.GetMetadata("upAxis"), - "meters_per_unit": stage.GetMetadata("metersPerUnit"), -}) -``` +`scripts/usd_validation_executor.py` emits a `coverage_ledger` in every +`validation-report.json`. Each flagged `(target, concept)` from the scope note +must appear with a resolved status: +`probed_with_findings | probed_clean | user_declined | timeout_recorded | +blocked_validation_runtime`. `coverage_ledger.complete` is `true` only when no +flagged target is unresolved, and the report `summary.status` is `BLOCKED` +until it is. **Do not advance to the optimization report or declare the +iteration done while the ledger is incomplete.** -For SO-specific validators, follow `so-run-validators`; SO validator rules -auto-register via `@register_rule` decorators when both packages share the -same Python environment. +## Tier Decision Inputs -### Programmatic per-rule API +No schema contains a single `tier` field. Tier selection is policy applied to +the structure-assessment and validator reports: -For one-rule-at-a-time validation (used by the per-rule timeout pattern in -`references/validation-scoping.md`), drive the engine directly: +| Source | Fields | How they affect tier/scoping | +|---|---|---| +| `usd-structure-assessment-report.schema.json` | `phase_recommendation` | Selects the default validation posture: `structuring`, `optimization`, or `already_optimized`. | +| `usd-structure-assessment-report.schema.json` | `summary_counts.prim_count`, `summary_counts.mesh_count`, `summary_counts.prototype_count`, `summary_counts.instance_count`, `summary_counts.reference_count`, `summary_counts.payload_count` | Determines large-stage status and whether Tier 2/3 must run per target, sampled, or not at all. | +| `usd-structure-assessment-report.schema.json` | `validation_scope.per_asset`, `validation_scope.cross_component_pairs`, `validation_scope.skip` | Defines the concrete target set for Tier 2 and Tier 3. | +| `usd-structure-assessment-report.schema.json` | `flagged_assets`, `findings`, `hierarchy_dedupe.recommended`, `hierarchy_dedupe.top_candidates` | Supplies reasons to include targeted Tier 2 probes or to ask for Tier 3 probes. | +| `validator-concepts.json` | `tier`, `cost_class`, `gpu_bound`, `scope_policy` per canonical concept | Single source of truth for a concept's tier and scope. Read it; do not restate tiers elsewhere. | +| `rule-reference.md` | Validator signal → canonical concept → backing op | Interpretation map only (signal to concept to fix op). Carries no tier. | +| `validation-report.schema.json` | `validators[].canonical_name`, `validators[].status`, `validators[].issues`, `summary.errorCount`, `coverage_ledger` | The canonical executor's own report — what ran (by canonical concept and resolved `(module, class_name)` identity) and what was found. Use it to narrow later iterations, not to widen scope silently. | + +Selected validators are named by **canonical concept name** (e.g. +`primvar_indexability`, `geom_duplicates`), defined in +`references/validator-concepts.json`. The canonical executor resolves each +concept to a unique `(module, class_name)` identity at run time. Do not put +runtime class names (`IndexedPrimvarChecker`), operation names, display labels, +or category names (`Geometry`, `Usd:Performance`) in the plan — class names are +not unique across providers and categories are lookup buckets, not approval +scope. The registry's `preferred_provider` decides Scene Optimizer vs Asset +Validator; performance tuning prefers the Scene Optimizer implementation. + +## Phase-Aware Defaults + +| `phase_recommendation` | Default scope | +|---|---| +| `structuring` | Minimum-openability + targeted structural blockers only. Do not validate geometry about to be restructured. | +| `optimization` | Minimum-openability + Tier 1 cheap whole-stage stats/probes + Tier 2 on flagged targets or sample. Tier 3 scoped probes mandatory on flagged targets/pairs; full-stage Tier 3 after approval. | +| `already_optimized` | Minimum-openability + Tier 1 cheap whole-stage stats/probes only; ask before expanding. | +| missing | Run structure assessment first. Do not begin with validators. | + +## Deterministic Selection + +Selection is a **function of structure-assessment evidence, not agent +judgment**. Two runs over the same SA report must select the same concept set, +because disagreement between runs is the variance this runner exists to remove. +Apply the table top-to-bottom; each matched row contributes its concepts. Do not +add concepts that no row selects, and do not drop a concept a row selects. Tier +and scope policy for each concept come from `validator-concepts.json` (the +"Target" column states only the selection granularity, not the tier). + +| SA signal (condition) | Concepts selected | Target | +|---|---|---| +| Always (any `optimization`/`already_optimized` run) | `composition_missing_ref`, `material_path`, `material_dangling_binding`, `texture_bind`, `texture_normalmap` | whole-stage safety gate | +| `phase_recommendation = optimization` | `material_duplicates`, `structure_empty_leaf`, `structure_invisible`, `structure_flat_hierarchy`, `extents_zero`, `perf_small_mesh`, `perf_sparse_mesh`, `perf_rtx_mesh_count`, `perf_redundant_timesamples`, `perf_high_vertex_count` | whole stage | +| Asset posture is CAD / BIM / MEP / converted (e.g. Revit/HOOPS) | `primitive_fit` | per flagged target — **mandatory**, never dropped | +| `flagged_assets[*]` primvar/UV signal | `primvar_indexability`, `primvar_unused` | per flagged asset | +| `flagged_assets[*]` mesh-hygiene signal (welds/degenerate/winding) | `vertex_weld`, `topology_zero_area_faces`, `normals_winding` | per flagged asset | +| `hierarchy_dedupe.recommended` or duplicate-geometry signal | `geom_duplicates` (+ `geom_duplicates_fuzzy` if near-duplicates) | flagged subtree | +| `validation_scope.cross_component_pairs[*]` with `enclosure_opaque: true` | `spatial_occluded` | flagged pair — **mandatory** scoped probe | +| `validation_scope.cross_component_pairs[*]` (routing/overlap) | `spatial_overlapping`, `spatial_coinciding` | flagged pair — **mandatory** scoped probe | +| Target is simulation-ready (physics/Boolean/3D-print), not visualization | `topology_manifold`, `normals_validity` | flagged target | + +If `validation_scope.skip` lists a target, it is excluded from all rows. If no +asset is flagged, only the "Always" + whole-stage rows fire; ask before adding more. + +## Iteration Subtraction + +Re-validation in later iterations is **same-or-narrower by construction**: + +- Start from the previous iteration's selected concept set. +- **Subtract** every `(target, concept)` whose ledger status was `probed_clean` + or that a completed operation resolved. Resolved-clean targets are not + re-probed. +- **Keep** targets that were `probed_with_findings` (re-verify the fix), + `timeout_recorded` (retry masked/standalone), or regressed. +- **Never widen** to new Tier 3 targets/pairs, new concepts no SA row selects, + or full-stage scope without explicit user approval. +- Keep the FIRST pass's baseline metrics; do not re-baseline. + +This makes each pass cheaper and convergent, and guarantees a later run cannot +silently disagree with an earlier one by re-expanding scope. + +## Scoping Rules + +1. Structure assessment is the first filter. Use `summary_counts`, + duplicate-hierarchy candidates, `validation_scope`, and `flagged_assets` to + decide which validators can change the optimization plan. +2. **Which concepts to run is decided by Deterministic Selection above; tier and + scope policy come from `validator-concepts.json`.** This section does not + re-derive selection or tiering. +3. Do not start performance work with a full default AV sweep. +4. Keep SO analysis in the validation workflow. Importing SO validators makes + rules discoverable; it does not authorize running all of them. +5. For cross-component validators, use `Usd.Stage.OpenMasked()` covering only + the flagged pair and dependency closures, or validate standalone target files. +6. Do not run noisy/slow concepts globally in Phase 2c. Any registry concept + that is `gpu_bound`, `cost_class: expensive`, or `stage_dependent` is scoped + to flagged targets/pairs only — never a full-stage default. +7. Category-scoped AV is still a scoped whole-stage traversal for that category. + On large stages, ask before full sweep and prefer masked spot checks or + bounded parallel subprocesses with timeouts. +8. Prefer summaries over issue dumps. Apply + `runtime-artifact-token-budget.md` for CSV/log/summary handling. + +## Selected-Rule Execution Pattern + +Do not use `ValidationEngine()` or +`ValidationEngine(init_rules=True)` unless the user explicitly approved +exhaustive validation. That pattern runs every registered OAV rule plus every SO +validator that auto-registered. + +Execution model: + +- **Tier 1:** run selected cheap whole-stage stats/probes in one batch for the + scoped target. This is not a default all-rules sweep. +- **Tier 2:** run selected rules per target in isolated OS subprocesses with an + explicit wall-clock timeout. Parallelize independent target/rule subprocesses + within resource budget. +- **Tier 3:** ask first, then use the same subprocess pattern on flagged targets + only. +- **Timeout fallback:** if a Tier 2 or Tier 3 rule times out, record a timeout + finding and rerun a masked-stage spot sample or standalone payload/prototype + sample instead of widening to a full sweep. +- **Do not batch Tier 2/3 rules in one engine** unless the target is small and + the user explicitly accepted the risk. One slow C++ rule can dominate or hang + the whole batch, and Python `signal.alarm` or threads may not interrupt it. + +Inside Kit, import `omni.asset_validator.core` instead of +`omni.asset_validator`, but keep the same selected-rule posture. Ask before full +sweep before any copyable pattern that enables default/all rules. + +### Canonical executor (the only supported runner) + +The runner ships a canonical executor at `scripts/usd_validation_executor.py`. +**Call it directly — do not reimplement rule resolution and do not write your +own script.** It resolves each canonical concept to a unique `(module, +class_name)` via `references/validator-concepts.json`, enables exactly those +rule classes (never `init_rules=True`), and opens the stage scoped. It is +fail-closed by contract: unknown concept, ambiguous identity, unregistered rule, +or missing runtime all raise — there is no bare-name lookup and no CLI fallback. +This is what disambiguates the Scene Optimizer `IndexedPrimvarChecker` (fast +triage) from the Asset Validator one (full audit) that share a class name. ```python -import omni.scene.optimizer.validators # auto-registers SO rules -from omni.asset_validator import CategoryRuleRegistry, ValidationEngine -from pxr import Usd - -registry = CategoryRuleRegistry() -rule = next( - rule - for rule in registry.get_rules("Usd:Performance") - if rule.__name__ == "PrimitiveFitChecker" +from usd_validation_executor import ( + load_registry, + validate_concepts, + ValidationRuntimeUnavailable, +) + +registry = load_registry() # references/validator-concepts.json + +# Tier 1: one batch over the SA-selected target. +issues = validate_concepts( + stage_path, + ["material_duplicates", "structure_empty_leaf"], + registry=registry, ) -engine = ValidationEngine(init_rules=False) -engine.enable_rule(rule) -stage = Usd.Stage.Open("path/to/asset.usd") -results = engine.validate(stage) # synchronous -for issue in results.issues(): - ... +# Tier 2 / Tier 3: one concept + target group per bounded subprocess. +issues = validate_concepts( + stage_path, + ["primvar_indexability"], + registry=registry, + mask_paths=["/World/Racks/Rack_A"], +) ``` -Inside Kit, import `omni.asset_validator.core` instead of `omni.asset_validator` (the `.core` submodule is the in-Kit name). The standalone package uses the plain `omni.asset_validator` import. +If the validation runtime cannot be imported, `validate_concepts` raises +`ValidationRuntimeUnavailable`; record `blocked_validation_runtime` in the +coverage ledger rather than fabricating a pass. -Use this API when you need: +Call `validate_concepts` once for a Tier 1 batch. For Tier 2 and Tier 3, run the +whole scope note through `run_scope_note` with the **subprocess** runner so each +concept executes in a killable child process and timeouts become a recorded +disposition rather than a hang: -- Per-rule timeouts on a large stage (see `references/validation-scoping.md` *Per-rule timeout pattern*). -- A single rule's findings without running the full default set. -- Cross-runtime parity checks (same stage, same rule, both Kit and standalone). +```python +from usd_validation_executor import run_scope_note, subprocess_concept_runner + +report = run_scope_note( + stage_path, + scope_note, # validation-scope-note.schema.json + registry=registry, + concept_runner=subprocess_concept_runner(timeout_seconds=120), + phase="baseline", +) +# report["coverage_ledger"]["complete"] gates "done"; timeouts -> timeout_recorded. +``` -Do not route validation through the Scene Optimizer package's bundled -`validator-venv` — it may lack `numpy`, making large-stage validation slow. -Use a project-managed venv with both `omniverse-asset-validator` and the SO -package on PYTHONPATH. +`subprocess_concept_runner` invokes this module as a child (`python +usd_validation_executor.py`, JSON job on stdin) — an internal worker protocol, +not a CLI. The default in-process runner is for Tier 1 only, where a hang is not +a risk. If a concept times out, `run_scope_note` records `timeout_recorded`; +retry that target with `mask_paths` from the spot-check policy below. + +## Masked-Stage Spot Checks + +Use masked-stage spot checks when full-stage or per-target validation is too +expensive but prim-level findings can still change the optimization plan. + +Use spot checks when: + +- Stage is large for validation planning. +- SA can identify representative candidate subtrees. +- Rule set is mostly prim-local geometry/material/schema checks. +- Tier 2 or Tier 3 subprocess validation times out. +- Result is optimization evidence, not formal conformance. + +Sample selection: + +1. Build a cheap whole-stage inventory first: top branches by mesh count, + semantic names, top prototype/fingerprint groups, material-heavy branches, + and instance-heavy branches. +2. Include all SA-flagged targets that may change the operation plan. +3. Cover at least 25% of mesh-bearing content by mesh count, `rtxMeshCount`, or + instance-proxy mesh contribution. If impractical, record why and mark the + result as limited sample evidence. +4. Include high-risk exemplars: largest mesh, deepest hierarchy, + material-heavy mesh, repeated module, top prototype/fingerprint family, and + dominant mesh-bearing semantic classes. +5. Add closure paths needed by the sample, such as material/looks scopes or + shared class/inherit sources. +6. Reject empty samples; if proxy/prototype-aware counts report 0 meshes, + resample instead of reporting "0 findings." + +Label output `scope: "masked_stage_spot_check"` with sampled paths, semantic +tags, mesh coverage percentage, and evidence scope. + +## Post-Restructure / Post-Decompose Validation Strategy + +After `apply-restructure` or `decompose-for-selective-loading` produces an +assembly root plus payload/prototype files, do not open the full composed stage +with all payloads loaded for a blanket validator sweep. + +- **Assembly skeleton:** open with `Usd.Stage.OpenMasked()` excluding payload + prim paths. Run structural validators only: reference resolution, kind + hierarchy, layer structure, defaultPrim, extent hints, assetInfo. +- **Assembly root as optimization target:** if it retains mesh content after + extraction, validate and optimize it like any other target using this policy. +- **Each payload/prototype:** open each file independently with + `Usd.Stage.Open(payload_file)`. Plan validation per target based on that + target's prim/mesh count. +- **Cross-payload pairs:** open with `Usd.Stage.OpenMasked(root, mask)` covering + only the relevant payload subtrees. Run Tier 3 only per flagged pair. + +Each target re-enters this runner independently; approval gates and spot-check +thresholds apply per target, not to the original composed stage. + +## Asset Validator Load Rules + +The Asset Validator's `ComplianceChecker` opens a new stage from the input's +root layer with default `LoadAll` semantics. Caller `StageLoadRules` such as +`LoadNone` are discarded. `StagePopulationMask` is preserved, so +`Usd.Stage.OpenMasked()` is the reliable scoping mechanism. + +Do not rely on `LoadNone` or `stage.Load(specific_path)` for validation +scoping. Use `OpenMasked` or validate standalone payload/prototype files. + +For small/medium stages, use the standard selected validation plan via +`so-run-validators`, but keep the same tier execution model: Tier 1 may batch; +Tier 2 and Tier 3 use bounded subprocesses. + +## Validation Plan Shape + +The plan is the scope note defined by `scripts/validation-scope-note.schema.json` +— there is no separate plan format. **Deterministic Selection** decides which +concepts the note contains; the registry supplies each concept's tier and scope +policy; masked spot-check fields are described under **Masked-Stage Spot +Checks**. + +## Routing Decision ---- +| Intent | Stacks | +|---|---| +| Validate this USD before mutation | Pre-mutation USD stack: minimum-openability plus targeted Asset Validator coverage when needed. | +| Broad performance ask | `usd-structure-assessment` first, then selected performance stack per this runner. Add pre-mutation USD stack only when validity affects mutation safety. | +| Run perf validators only | Performance stack only, selected from SA evidence or the user's explicit target list. | +| Validate optimized output | Same or narrower stacks than Phase 2c for fair comparison unless the user approves expansion. | +| Formal conformance/exhaustive validation | Ask before full sweep, then route through the selected AV/runtime with explicit timeout and artifacts. | -## Required gates +## Required Gates -Pre-processing (before any mutation): +Pre-processing: - Stage opens. - Asset paths resolve. -- Minimum-openability and selected Asset Validator checks complete. +- Minimum-openability and selected checks complete. - Known blocker findings are either fixed or waived. -Post-processing (Phase 6 re-validation): +Post-processing: - Stage opens. - Validation is no worse than baseline unless explicitly accepted. - Generated outputs are recorded. - Processor report and validation report are attached to the optimization plan. ---- - ## Output -Emit `validation-report.json` matching `scripts/validation-report.schema.json`. The report must point to provider artifacts including `issues.csv`, `provider-summary.json`, and `run.log`, and include the chosen tier/phase scoping so Phase 6 can reproduce it. +Emit `validation-report.json` matching `scripts/validation-report.schema.json` +when that report is produced. The report must point to provider artifacts such +as `issues.csv`, `provider-summary.json`, and `run.log`, and include the chosen +phase scoping so Phase 6 and Phase 7 can reproduce or narrow it. + +## Hard Rules + +1. Never run all validators on all assets by default. +2. Never use `ValidationEngine()` or `ValidationEngine(init_rules=True)` after + SO validator registration unless exhaustive validation was approved. +3. Never run Tier 3 without structural evidence from the assessment. +4. When SA flags a Tier 3 target, the **scoped** probe is mandatory and needs no + approval; ask only before the **full-stage** version. Silent omission of a + flagged expensive probe is a defect, not a cost saving. +5. Ask before full sweep on any large stage. +6. Never start a performance workflow with a full default AV sweep. +7. Prefer masked-stage spot checks over dropping validation when full-stage + validation is too expensive. +8. Run Tier 2 and Tier 3 validation through bounded subprocesses; if a rule + times out, record `timeout_recorded` and retry with a masked or standalone + sample — never silently drop the target. +9. Always report what was skipped and why; the user may override. +10. Never declare an iteration done while `coverage_ledger.complete` is `false`. + Every flagged `(target, concept)` must reach a resolved disposition first. ---- - -## Rules +## Troubleshooting -- Validation is evidence, not cleanup by itself. -- Convert findings into candidate operations via - `so-interpret-validators/references/rule-reference.md`; do not hand the - user raw validator jargon as the recommendation. -- Do not auto-fix without recording which layers or files changed. -- Do not continue after structural USD failures unless the plan is diagnosis-only. -- Do not call Scene Optimizer operation skills until validation evidence has been captured or the user has explicitly chosen to proceed without it. -- All cost-control rules (tiers, gates, thresholds) live in - `references/validation-scoping.md`. Do not invent thresholds inline. +- If `omni_asset_validate` is unavailable, record it as missing rather than + fabricating a pass. +- If Scene Optimizer validator imports fail, do not report SO-specific results. +- If the bundled `validator-venv` is slow or lacks dependencies, prefer a Kit or + project-managed Asset Validator environment. +- Named validator unavailable: record the gap and choose the nearest supported + source only when it answers the same scoped question. ## References -- `references/validation-scoping.md` - tier 1/2/3 tables, JSON plan template, scene-aware adjustment, phase-aware subset rules. Read this when building a perf-stack plan. -- `skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-run-validators/references/infrastructure.md` - SO validator infrastructure (programmatic API, REQUIRES_MESH cache, entry-point allow-list, CLI gotchas, libusd alignment). Read this when debugging SO validator setup. -- `skills/omniverse-usd-performance-tuning/references/workflow.md` - canonical 7-phase flow context for where validation sits. +- `references/validate-usd-asset-validator.md` - Asset Validator runtime + invocation details. +- `skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-run-validators/references/infrastructure.md` - SO validator infrastructure. +- `skills/omniverse-usd-performance-tuning/references/workflow.md` - canonical + 7-phase flow context for where validation sits. diff --git a/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-interpret-validators/README.md b/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-interpret-validators/README.md index 770d4cd4..476ef703 100644 --- a/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-interpret-validators/README.md +++ b/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-interpret-validators/README.md @@ -26,7 +26,8 @@ reads. Do not clone the source repo just to read upstream SO guidance. ## Local Responsibilities - Preserve logical milestone name `so-interpret-validators`. -- Use `validation-scoping.md` for tiering, phase-aware subsets, deferred validators, and approval gates. +- Use `usd-validation-runner/README.md` for tiering, phase-aware subsets, + selected-validator execution policy, and approval gates. - Use `rule-reference.md` only for local recommendation routing; upstream owns generic artifact interpretation mechanics. - Apply `runtime-artifact-token-budget.md` for CSV/log handling and route large artifacts through summaries. diff --git a/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-interpret-validators/references/follow-up-queries.md b/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-interpret-validators/references/follow-up-queries.md index 365cf555..7297935c 100644 --- a/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-interpret-validators/references/follow-up-queries.md +++ b/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-interpret-validators/references/follow-up-queries.md @@ -36,11 +36,11 @@ Look up the rule in the *Rule reference*. Then: > `` wraps ``. To apply the fix, invoke the `so-run-operations` > skill (Claude alias: `/so-run-operations --config '[{"operation":"", ...}]'`) > or call the operation directly via the Python bindings after probing the - > selected SO API surface. For the full invocation reference (runtime probe, - > chains via `executeConfig`, JSON pipelines via - > `standalone.execute_commands_from_json`, and - > `acquire_interface().execute_operation` fallback), see - > upstream `usd-optimize/.agents/operations/INVOCATION.md`. For output + > selected SO API surface. For the full invocation reference (runtime probe, + > chains via `executeConfig`, JSON pipelines via + > `standalone.execute_commands_from_json`, and the required + > `ExecutionContext` stage attachment), see + > `skills/omniverse-usd-performance-tuning/references/so-run-operations/references/invocation.md`. For output > Save-vs-Export policy and digitaltwin workspace rules, see > `skills/omniverse-usd-performance-tuning/references/usd-structure-assessment/references/usd-edit-target-planner/references/output-saving.md`. For > generic multi-op pipelines organized by bottleneck, see upstream @@ -101,9 +101,14 @@ re-run Steps 3 + 4. ### "Only check ``" (before a run) -The validator runner doesn't expose a `--rule` flag. Tell the user we'll run -the full default set and filter the CSV / summarizer output to that rule before -presenting. If they need to skip expensive rules, they can omit -`--include-expensive` (default). +Selecting which rules run is the canonical executor's job, keyed by **canonical +concept** — there is no `--rule` flag and no run-everything-then-filter step. +Map the rule the user named to its canonical concept in +`references/usd-validation-runner/references/validator-concepts.json`, then run +just that concept through the executor — `validate_concepts(stage, [concept])` +for a single target, or `run_scope_note(...)` for a scoped plan (see +`references/usd-validation-runner/README.md`). The executor enables only the +resolved rule class, so expensive rules are never pulled in unless their concept +is explicitly selected. --- diff --git a/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-interpret-validators/references/rule-reference.md b/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-interpret-validators/references/rule-reference.md index 702b319d..bac1d46b 100644 --- a/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-interpret-validators/references/rule-reference.md +++ b/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-interpret-validators/references/rule-reference.md @@ -3,113 +3,107 @@ # Validator Rule Reference -The mapping below is the local source of truth for the digitaltwin workflow's -`Fix tier` and `Operation` columns. Scene Optimizer validator mechanics and -operation docs live upstream in +This table maps a reported **validator signal** to its **canonical concept** +and the **backing operation** that fixes it. It is the interpretation source for +turning findings into op candidates — it is **not** an execution allowlist and +it does **not** publish tiers. + +**Single source of truth.** Validator *identity* (`module` + `class_name`), +*tier*, *scope policy*, and *preferred provider* live only in +`../../../validator-concepts.json` (keyed by canonical concept). Do not restate +tier numbers here or in the runner README; if a tier matters, read it from the +registry. Execution goes through `scripts/usd_validation_executor.py`, which +resolves the canonical concept to a unique registered rule class and fails +closed on anything unknown or ambiguous. Never copy a runtime class name (e.g. +`IndexedPrimvarChecker`) or a category (`Geometry`, `Usd:Performance`) into a +scope note — class names are not unique across providers. + +Scene Optimizer validator mechanics and operation docs live upstream in [usd-optimize](https://github.com/NVIDIA-omniverse/usd-optimize/) and the prebuilt Scene Optimizer package. Resolve guidance from an extracted package -root via `$SCENE_OPTIMIZER_PACKAGE_ROOT`, then `$SO_HOME`. If no package -root exists, download/extract the published `scene_optimizer_core_...release.zip` +root via `$SCENE_OPTIMIZER_PACKAGE_ROOT`, then `$SO_HOME`. If no package root +exists, download/extract the published `scene_optimizer_core_...release.zip` package (direct archive URLs are in `references/upstreams/usd-optimize.md`) or -use the package path, URL, or extracted root supplied by the user. Do not clone -the source repo just to read SO guidance. To verify a rule's backing operation, -inspect upstream -`source/core/python/omni/scene/optimizer/validators/.py`; the -authoritative registered-rule lists live in upstream `_default_rule_classes()` -and `_expensive_rule_classes()`. - -### SceneOptimizer rules (default) - -| Rule | Backing op | Tier | Notes | -|------|-----------|------|-------| -| SceneOptimizerCoincidingGeometryChecker | `findCoincidingGeometry` | T3 | Analysis-only; prefer `deduplicateGeometry` before destructive deletion. Use upstream `.agents/operations/findCoincidingGeometry.md` for mechanics. | -| SceneOptimizerColocatedVerticesChecker | `meshCleanup` | T1 | `meshCleanup` merges colocated vertices. | -| SceneOptimizerDuplicateFacesChecker | `meshCleanup` | T1 | `meshCleanup` removes duplicate faces. | -| SceneOptimizerDuplicateGeometryChecker | `deduplicateGeometry` | T1 | Converts identical meshes to USD instances. | -| SceneOptimizerDuplicateHierarchiesChecker | `usd-hierarchy-dedupe-candidates` + `apply-restructure` | T3 | Structural signal; use the hierarchy candidate finder + restructure gate, not a direct mesh op. | -| SceneOptimizerDuplicateMaterialsChecker | `optimizeMaterials` | T1 | Merges duplicate material definitions. | -| SceneOptimizerEmptyLeafChecker | `pruneLeaves` | T1 | Removes leaf prims with no geometry. | -| SceneOptimizerFlatHierarchiesChecker | `findFlatHierarchies` | T3 | Analysis-only. Fix: `flattenHierarchy` operation. | -| SceneOptimizerFlattenHierarchyChecker | `flattenHierarchy` | T2 | Has params; tune using upstream `.agents/operations/flattenHierarchy.md`. | -| SceneOptimizerFuzzyDuplicateGeometryChecker | `deduplicateGeometry` | T1 | Same op, different threshold. | -| SceneOptimizerIndexedPrimvarChecker | `optimizePrimvars` | T1 | Converts to indexed primvars; may be deferred when the op is pre-selected. See [`validation-scoping.md`](../../validation-scoping.md) -> Deferred. | -| SceneOptimizerInvisiblePrimsChecker | `removePrims` | T2 | Confirm intent before removing — invisible may be deliberate. | -| SceneOptimizerIsolatedVerticesChecker | `meshCleanup` | T1 | `meshCleanup` removes isolated verts. | -| SceneOptimizerMeshDensityChecker | `countVertices` | T2 | Informational; use lossless reducers first, decimate only after the `decimateMeshes` upfront prompt. | -| SceneOptimizerNonManifoldChecker | `meshCleanup` | T2 | Mesh topology may need repair (non-manifold geometry); skip for visualization-only workflows. See [`validation-scoping.md`](../../validation-scoping.md) -> Deferred. | -| SceneOptimizerNormalsChecker | `generateNormals` | T1 | Regenerates missing/invalid normals. | -| SceneOptimizerPrimitiveFitChecker | `fitPrimitives` | T2 | Primitive replacement; use upstream `.agents/operations/fitPrimitives.md` for parameter semantics. | -| SceneOptimizerRedundantTimeSamplesChecker | `optimizeTimeSamples` | T1 | Removes redundant samples on animated attributes. | -| SceneOptimizerRtxMeshCountChecker | `rtxMeshCount` | T2 | Informational threshold check. Reduce mesh count via `deduplicateGeometry` + `flattenHierarchy` + `removeSmallGeometry`. | -| SceneOptimizerSmallMeshChecker | `removeSmallGeometry` | T1 | Removes meshes below a screen-space threshold. | -| SceneOptimizerSparseMeshChecker | `sparseMeshes` | T2 | Tune density thresholds. | -| SceneOptimizerUnusedUVsChecker | `removeUnusedUVs` | T1 | Removes unbound UV sets; may be deferred when the op is pre-selected. See [`validation-scoping.md`](../../validation-scoping.md) -> Deferred. | -| SceneOptimizerWindingsChecker | `meshCleanup` | T1 | Fixes inconsistent face winding. | -| SceneOptimizerZeroAreaFacesChecker | `meshCleanup` | T1 | Removes degenerate faces. | -| SceneOptimizerZeroExtentChecker | `removeSmallGeometry` | T1 | Analysis finds zero-extent meshes; fix removes them. Use `computeExtents` first when the issue is stale metadata. | - -### SceneOptimizer rules (expensive — only present with `--include-expensive`) - -| Rule | Backing op | Tier | Notes | -|------|-----------|------|-------| -| SceneOptimizerOccludedMeshesChecker | `findOccludedMeshes` → `removePrims` | T3 | **Two-step detect→act chain.** Analysis identifies fully-occluded prim paths; feed those paths to `removePrims` for deletion. Runs FIRST in Phase 4 op chain (before meshCleanup, dedupe, decimate). Scoped to SA containment pairs with `enclosure_opaque: true` only — transparent enclosures are excluded. Two-stage user approval: (1) approve analysis cost, (2) approve deletion of discovered internals. | -| SceneOptimizerFindOverlappingMeshesChecker | `findOverlappingMeshes` | T3 | Analysis-only. Fix: review and remove/merge in DCC. | - -### Base asset-validator rules (`omni.asset_validator.DefaultPlugin`) - -The full list lives in the upstream `omniverse-asset-validator` package; we don't -mirror it here. Many base rules detect issues that map cleanly onto a Scene -Optimizer operation — surface the equivalent op so the user has an automated fix -path even when the rule itself is upstream. - -**Stage / metadata (no SO equivalent — manual fix):** - -- `KindChecker`, `DefaultPrimChecker`, `StageMetadataChecker` — stage-metadata - rules. **T3 / manual.** Fix via USD Python API: - `stage.SetDefaultPrim(...)`, `prim.SetMetadata('kind', 'component')`, - `UsdGeom.SetStageUpAxis(...)`, etc. Check the CSV `Suggestion` column. -- `OmniOrphanedPrimChecker`, `OmniDefaultPrimChecker` — Omni-flavored variants. - T3 / manual. -- `LayerSpecChecker` — type/value mismatches in layer specs. T3 / manual. - -**External references (no SO equivalent — manual fix):** - -- `MissingReferenceChecker` — unresolvable references. T3 / manual. Common cause: - asset flattened on another machine with absolute paths. Fix by re-flattening - with the textures available, or rewriting absolute paths to relative. -- `MaterialPathChecker` — `info:mdl:sourceAsset` attributes pointing at missing - files. T3 / manual. Same root cause as `MissingReferenceChecker`. -- `NormalMapTextureChecker` — `UsdUVTexture inputs:file` unresolvable. T3 / manual. +use the package path supplied by the user. To verify a rule's backing +operation, inspect upstream +`source/core/python/omni/scene/optimizer/validators/.py`. + +### Scene Optimizer rules (default) + +| Validator signal | Canonical concept | Backing op | Notes | +|------|------|-----------|-------| +| SceneOptimizerCoincidingGeometryChecker | `spatial_coinciding` | `findCoincidingGeometry` | Analysis-only; prefer `deduplicateGeometry` before destructive deletion. | +| SceneOptimizerColocatedVerticesChecker | `vertex_weld` | `meshCleanup` | Merges colocated vertices. | +| SceneOptimizerDuplicateFacesChecker | `topology_duplicate_faces` | `meshCleanup` | Removes duplicate faces. | +| SceneOptimizerDuplicateGeometryChecker | `geom_duplicates` | `deduplicateGeometry` | Converts identical meshes to USD instances; run per target or sample, never an unbounded whole-stage default. | +| SceneOptimizerDuplicateHierarchiesChecker | _(structural — no mesh concept)_ | `usd-hierarchy-dedupe-candidates` + `apply-restructure` | Use the hierarchy candidate finder + restructure gate, not a direct mesh op. | +| SceneOptimizerDuplicateMaterialsChecker | `material_duplicates` | `optimizeMaterials` | Merges duplicate material definitions. | +| SceneOptimizerEmptyLeafChecker | `structure_empty_leaf` | `pruneLeaves` | Removes leaf prims with no geometry. | +| SceneOptimizerFlatHierarchiesChecker | `structure_flat_hierarchy` | `findFlatHierarchies` → `flattenHierarchy` | Analysis-only signal; fix is the `flattenHierarchy` operation. | +| SceneOptimizerFuzzyDuplicateGeometryChecker | `geom_duplicates_fuzzy` | `deduplicateGeometry` | Same op, different threshold; run per target or sample. | +| SceneOptimizerIndexedPrimvarChecker | `primvar_indexability` | `optimizePrimvars` | Converts to indexed primvars when the result can change the op plan. | +| SceneOptimizerInvisiblePrimsChecker | `structure_invisible` | `removePrims` | Confirm intent before removing — invisibility may be deliberate. | +| SceneOptimizerIsolatedVerticesChecker | `topology_isolated_vertices` | `meshCleanup` | Removes isolated verts. | +| SceneOptimizerMeshDensityChecker | `perf_high_vertex_count` | `countVertices` | Informational; lossless reducers first, `decimateMeshes` only after the upfront tolerance prompt. | +| SceneOptimizerNonManifoldChecker | `topology_manifold` | `meshCleanup` | Skip for visualization-only workflows; run only for simulation-ready intent. | +| SceneOptimizerNormalsChecker | `normals_validity` | `generateNormals` | Regenerates missing/invalid normals; targeted check only. | +| SceneOptimizerPrimitiveFitChecker | `primitive_fit` | `fitPrimitives` | Bounded-loss; requires the tolerance prompt before applying. Highest-value reducer for converted CAD/BIM content. | +| SceneOptimizerRedundantTimeSamplesChecker | `perf_redundant_timesamples` | `optimizeTimeSamples` | Removes redundant samples on animated attributes. | +| SceneOptimizerRtxMeshCountChecker | `perf_rtx_mesh_count` | `rtxMeshCount` | Informational threshold check. Reduce via `deduplicateGeometry` + `flattenHierarchy` + `removeSmallGeometry`. | +| SceneOptimizerSmallMeshChecker | `perf_small_mesh` | `removeSmallGeometry` | Removes meshes below a screen-space threshold. | +| SceneOptimizerSparseMeshChecker | `perf_sparse_mesh` | `sparseMeshes` | Tune density thresholds. | +| SceneOptimizerUnusedUVsChecker | `primvar_unused` | `removeUnusedUVs` | Removes unbound UV sets when the result can change the op plan. | +| SceneOptimizerWindingsChecker | `normals_winding` | `meshCleanup` | Fixes inconsistent face winding. | +| SceneOptimizerZeroAreaFacesChecker | `topology_zero_area_faces` | `meshCleanup` | Removes degenerate faces. | +| SceneOptimizerZeroExtentChecker | `extents_zero` | `removeSmallGeometry` | Fix removes zero-extent meshes. Use `computeExtents` first when the cause is stale metadata. | + +### Scene Optimizer rules (expensive — only present with `--include-expensive`) + +| Validator signal | Canonical concept | Backing op | Notes | +|------|------|-----------|-------| +| SceneOptimizerOccludedMeshesChecker | `spatial_occluded` | `findOccludedMeshes` → `removePrims` | **Two-step detect→act.** Analysis identifies fully-occluded prim paths; feed those to `removePrims`. Runs first in the Phase 4 op chain. Scope to SA containment pairs with `enclosure_opaque: true`. Two-stage approval: (1) analysis cost, (2) deletion. | +| SceneOptimizerFindOverlappingMeshesChecker | `spatial_overlapping` | `findOverlappingMeshes` | Analysis-only. Fix: review and remove/merge in DCC. | + +These expensive concepts are `gpu_bound` and Tier 3 in the registry; they must be +scoped to flagged pairs (`paths=` / `OpenMasked`) and run in bounded +subprocesses — never full-stage by default on large CAD/BIM/MEP assets. + +### Asset Validator (OAV) base rules + +The full list lives in the upstream `omniverse-asset-validator` package; we +mirror only the concepts that participate in the performance workflow. Many base +rules map onto a Scene Optimizer operation — surface the equivalent op so the +user has an automated fix path even when the rule itself is upstream. **Geometry rules with SO operation equivalents:** -| Base rule | Equivalent SO op | Tier | -|-----------|------------------|------| -| `ExtentsChecker` | `computeExtents` | T1 | -| `IndexedPrimvarChecker` | `optimizePrimvars` | T1 | -| `WeldChecker` | `meshCleanup` (welds colocated verts) | T1 | -| `NormalsValidChecker` | `generateNormals` | T1 | -| `ZeroAreaFaceChecker` | `meshCleanup` | T1 | -| `UnusedMeshTopologyChecker` | `meshCleanup` (removes unreferenced points) | T1 | -| `ManifoldChecker` | `meshCleanup` (some topology repairs need DCC work) | T2 | - -**Deferral** (mirrors the SceneOptimizer table above): - -- `IndexedPrimvarChecker` — defer to the post-op pass when `optimizePrimvars` - is pre-selected by the assessment (CAD/BIM); the checker would just confirm - what the assessment already told you. -- `ManifoldChecker` — skip entirely for visualization-only workflows - (digital twins, AEC, rendering). Run only when the user's target is - simulation-ready (physics, Booleans, 3D printing). - -See `usd-validation-runner/references/validation-scoping.md` -> Deferred for -the conditional logic. - -When marking these in the summary table, label the tier as `T1-equiv` / -`T2-equiv` so the user knows the fix is a Scene Optimizer op, not the -validator's own `--fix` (this repo's validators don't ship a `--fix` mode). - -For rules not in this list, treat as **T3 / manual** and surface the CSV -`Suggestion` column verbatim. Don't invent fix commands. +| OAV base rule | Canonical concept | Backing op | Notes | +|-----------|------|------------------|------| +| `ExtentsChecker` | `extents_general` | `computeExtents` | Broader than SO `ZeroExtentChecker`. | +| `IndexedPrimvarChecker` | `primvar_indexability` (oav impl) | `optimizePrimvars` | **OAV variant is the slow full audit.** Registry tiers the OAV implementation higher than the SO triage one; the executor picks the SO impl for performance tuning. | +| `WeldChecker` | `vertex_weld` | `meshCleanup` | Welds colocated verts. | +| `NormalsValidChecker` | `normals_validity` | `generateNormals` | Targeted check only. | +| `ZeroAreaFaceChecker` | `topology_zero_area_faces` | `meshCleanup` | — | +| `UnusedMeshTopologyChecker` | `topology_unused_mesh` | `meshCleanup` | Removes unreferenced points. | +| `ManifoldChecker` | `topology_manifold` | `meshCleanup` | Some topology repairs need DCC work; skip for visualization-only targets. | + +**Stage / metadata / external references (safety gates — manual fix, no SO op):** + +| OAV base rule | Canonical concept | Notes | +|-----------|------|------| +| `KindChecker` | `kind_metadata` | Fix via `prim.SetMetadata('kind', ...)`. | +| `DefaultPrimChecker` | `layout_default_prim` | Fix via `stage.SetDefaultPrim(...)`. | +| `StageMetadataChecker` | `stage_metadata` | Fix via `UsdGeom.SetStageUpAxis(...)`, etc. | +| `LayerSpecChecker` | `layer_spec_health` | Type/value mismatches in layer specs. | +| `MissingReferenceChecker` | `composition_missing_ref` | Unresolvable references — common on assets flattened elsewhere with absolute paths. High-priority gate for conversions. | +| `MaterialPathChecker` | `material_path` | `info:mdl:sourceAsset` pointing at missing files. | +| `NormalMapTextureChecker` | `texture_normalmap` | `UsdUVTexture inputs:file` unresolvable. | + +For OAV-equivalent fixes, label the op as a Scene Optimizer operation (not the +validator's own `--fix` — this repo's validators don't ship a `--fix` mode). + +For any signal not in this list, treat it as a **manual fix** and surface the +CSV `Suggestion` column verbatim. Don't invent fix commands, and don't assign a +tier here — if the concept matters, add it to `validator-concepts.json`. --- diff --git a/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-run-validators/README.md b/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-run-validators/README.md index 2067bfb3..48c5ee81 100644 --- a/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-run-validators/README.md +++ b/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-run-validators/README.md @@ -16,7 +16,7 @@ when a user directly asks to run Scene Optimizer validators on a USD asset. 1. If this is the entry reference, run the local runtime gate and consume `/setup-preflight.json` before validation. -2. Apply `validation-scoping.md`, deferred-validator policy, and explicit +2. Apply `usd-validation-runner/README.md` selected-scope policy, deferred-validator policy, and explicit approval for expensive checks. 3. Apply `runtime-artifact-token-budget.md`; never read full validator CSVs or full `run.log` into context. @@ -55,7 +55,7 @@ reads. Do not clone the source repo just to read upstream SO guidance. - Runtime context gate and `setup-preflight.json` consumption. - `operationsAvailable` and runtime-family awareness from setup. -- Validation scoping, deferred validators, masked-stage spot-check policy, and +- Validation scoping, selected validators, masked-stage spot-check policy, and expensive-check approval gates. - Runtime artifact token budget for CSV/log/summary handling. - Digitaltwin milestone routing into `so-interpret-validators`. diff --git a/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-run-validators/references/infrastructure.md b/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-run-validators/references/infrastructure.md index ebf90938..d61b5306 100644 --- a/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-run-validators/references/infrastructure.md +++ b/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/so-run-validators/references/infrastructure.md @@ -25,5 +25,5 @@ reads. Do not clone the source repo just to read upstream SO guidance. ## Local Responsibilities -- Local validation scope, phase-aware subsets, and expensive-check gates remain in `validation-scoping.md`. +- Local validation scope, phase-aware subsets, and expensive-check gates remain in `usd-validation-runner/README.md`. - Setup/install references own runtime selection and `setup-preflight.json` writer behavior. diff --git a/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/validate-usd-asset-validator.md b/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/validate-usd-asset-validator.md index 8e85b1e6..15f843a6 100644 --- a/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/validate-usd-asset-validator.md +++ b/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/validate-usd-asset-validator.md @@ -5,331 +5,121 @@ ## Purpose -Use this validation reference when a stage or asset needs executable NVIDIA -Omniverse Asset Validator coverage. This is a validation-only reference: it -reports issues and recommended next steps, but does not apply fixes unless -explicitly requested. +Run the selected NVIDIA Omniverse Asset Validator checks from the +`usd-validation-runner` scope note and summarize findings. This reference owns +runtime invocation only; scoping, approval gates, full-sweep policy, masked +spot-check policy, and large-stage thresholds live in +`../README.md`. ## Prerequisites -- Run `setup-usd-performance-tuning` first to select a Kit or standalone validation runtime. -- Confirm the target is a USD asset path or asset directory. If basic USD - viability is unknown, first perform the runner's minimum-openability check: - stage opens, root/default prim is valid enough for the task, asset paths - resolve, and scale/up-axis metadata is known or recorded as missing. -- Require either a Kit runtime with `omni.asset_validator.core` or a standalone `omniverse-asset-validator` environment. Install or select a runtime before reporting `blocked_missing_dependency`. -- For unknown, customer-scale, or structurally large assets, require explicit - user approval before running the default full-stage Asset Validator rule set. - The default path is minimum-openability plus targeted rules/categories. +- `setup-usd-performance-tuning` has selected a Kit or standalone validation + runtime. +- Minimum USD openability has passed, or the runner explicitly asked this + reference to perform only that runtime check. +- The Phase 2c scope note names selected rules, target paths or masks, + skipped/approved full-sweep status, and artifact paths. +- Runtime artifact handling follows `../../runtime-artifact-token-budget.md`. -## Runtime Selection - -First resolve the runtime with `setup-usd-performance-tuning`. There are two -supported paths: - -- **Kit path**: use the selected Kit/USD Composer/Kit venv runtime and run - Asset Validator through `omni.asset_validator.core` inside that same Kit - process. Do not require `uv` or `omni_asset_validate` on `PATH` for this path. -- **Standalone path**: use a project-managed `omniverse-asset-validator` - environment. If that standalone environment is missing, invoke - `install-asset-validator-standalone`. - -Do not use the Scene Optimizer package's bundled -`validator-venv/bin/omni_asset_validate` as the preferred Asset Validator -runtime. That venv is package-local and has been observed without `numpy`, -which can make validation much slower on large USD stages. Prefer a -project-managed environment that can import both `omniverse-asset-validator` -and the selected Scene Optimizer package. - -Report `blocked_missing_dependency` only when neither path is available and the -missing runtime cannot be installed or selected. - -## Kit Path - -Use this path when setup returned `ready-kit`, when SO validators are needed, or -when validating remote `omniverse://` assets from the same authenticated Kit -runtime. - -> **Standalone is the preferred runtime.** The standalone path is lighter (no -> Kit overhead), deterministic, and achieves the same validator coverage: SO -> validators auto-register via `@register_rule` decorators when both -> `omniverse-asset-validator` and the SO package are importable in the same -> Python environment. Fall back to Kit when standalone is unavailable or when -> render-time profiling is needed (Kit-only capability). - -1. Use the Python launcher selected by setup: - - Classic Windows Kit: `\python.bat` - - Classic Linux Kit: `/python.sh` or `/python` - - Windows Kit venv: `\Scripts\python.exe` - - Linux Kit venv: `/bin/python` -2. Set `OMNI_KIT_ACCEPT_EULA=yes`. -3. Start Kit with `--no-window` and `--enable omni.asset_validator.core`. -4. Validate through `omni.asset_validator.core.ValidationEngine`. - -Minimal Kit pattern: - -```python -import os - -os.environ.setdefault("OMNI_KIT_ACCEPT_EULA", "yes") - -try: - from kit_app import KitApp -except ImportError: - from omni.kit_app import KitApp - -app = KitApp() -app.startup(["--no-window", "--enable", "omni.asset_validator.core"]) - -from omni.asset_validator.core import ValidationEngine - -asset_abs = "/path/to/asset.usd" -engine = ValidationEngine() -engine.disable_all_rules() -engine.enable_rules("omni.asset_validator.DefaultPlugin.StageMetadataChecker") -results = engine.validate(asset_abs) -issues = results.issues() if callable(results.issues) else results.issues - -print("ASSET_VALIDATOR_ISSUES", len(issues)) -app.shutdown() -``` - -If Scene Optimizer performance validators are also required, use -`so-run-validators` after setup verifies `omni.scene.optimizer.core` in the same -Kit runtime. - -## Standalone Path - -Use this path when the user chooses standalone validation or no Kit runtime is -available. Use a project-managed `omniverse-asset-validator` install created or -verified by `install-asset-validator-standalone` in the same venv where the SO -package is on PYTHONPATH. SO validators auto-register via `@register_rule` -decorators: no manual `register_all()` call is needed for rule discovery. For -targeted or category-scoped runs, select registered rule classes with -`CategoryRuleRegistry` and enable them with `ValidationEngine(init_rules=False)` -plus `enable_rule()`. +## Workflow -Do not use the SO bundled `validator-venv` — it may lack `numpy` and is -slower on large stages. +1. Use the runtime selected by setup; do not invent or switch runtimes. +2. Probe the selected CLI/API help before choosing flags or output formats. +3. Enable only the rules named in the scope note. +4. Ask before full sweep if the runner scope note does not already record + explicit exhaustive approval. +5. Store raw outputs on disk and write a compact summary before reading results + into context. +6. Feed summarized findings to `so-interpret-validators` and the optimization + report. -## Workflow +## Runtime Selection -1. Confirm the input is a USD asset path or an asset directory. -2. Confirm the minimum-openability check has passed, or run it first when - basic USD viability is unknown. -3. Choose the narrowest Asset Validator scope that answers the request: - targeted rule, targeted category, or explicit full-stage pass. For - performance work, prefer rules/categories that can change the - optimization plan; do not use a full-stage default sweep as the - preliminary baseline. -4. For large assets where full-stage coverage is too expensive but validation - signal is still useful, use the *Masked-Stage Spot Check Pattern* below on - representative prims/subtrees before escalating to a full sweep. -5. If the chosen scope is a default full-stage pass on an unknown, - customer-scale, or structurally large asset, stop and ask for approval. - Name the risk plainly: long runtime, high log/report volume, and possible - repeated issue rows. Offer targeted category/rule checks as the recommended - alternative. -6. Run validation with `validate-usd-asset-validator`. -7. Normalize issues by severity, rule, message, location, and suggested fix - when available. For high-volume findings, summarize by rule and target - class rather than dumping every repeated issue into the user-facing report. -8. If the raw Asset Validator report is large, summarize it with the - *Large Report Summarization* policy below before reading results into - context. -9. Fail the report on Asset Validator errors or failures. -10. Warn on Asset Validator warnings unless the active workflow profile promotes them to failures. -11. Hand off performance-oriented findings to `so-run-validators` / - `so-interpret-validators`. For external domain profiles or package - conformance, route to the owning external workflow rather than adding those - references here. - -## Timeout and Approval Policy - -All validation cost-control policy (full-sweep approval gate, timeout -guidance, spot-check thresholds) lives in -`validation-scoping.md` → *Full-Sweep Approval Gate* and -*Decision Tree*. Apply those rules before any full-stage invocation. - -When a timeout is used, record `timeout_seconds` and `timeout_reason`. If the -validator times out, report `status: TIMEOUT` and keep partial logs/artifacts -for follow-up. - -## Standalone CLI Pattern - -Project-managed standalone environment: - -```bash -omni_asset_validate --help -omni_asset_validate asset.usda --category Geometry --csv-output geometry-report.csv -``` +| Runtime | Use | Notes | +|---|---|---| +| Kit | Setup selected Kit, USD Composer, or a Kit venv; remote `omniverse://` validation; or same-runtime Scene Optimizer validation. | Import `omni.asset_validator.core` inside the selected Kit process. Do not require `uv` or `omni_asset_validate` on `PATH`. | +| Standalone | Setup selected a project-managed `omniverse-asset-validator` environment. | Use the selected Python/CLI. Do not use the Scene Optimizer package's bundled `validator-venv` as the preferred runtime. | -Use only the output flags advertised by the selected runtime. Some versions -or wrappers also support JSON output; when they do not, CSV is the -conservative standalone artifact. +Report `blocked_missing_dependency` only when setup cannot provide either +runtime and the user did not approve installation or selection. -Treat category-scoped CLI invocations as scoped full-stage traversals. On large -or unknown-size assets, apply `validation-scoping.md` first: prefer masked -spot checks, or run each category/rule with an explicit wall-clock budget and -record any timeout. +## Runtime detection (not rule selection) -Do not use a bare default invocation such as -`omni_asset_validate asset.usda --csv-output asset-validator-report.csv` on a -large or unknown asset until the full-sweep approval gate has passed. Do not use -`--fix` unless the user explicitly asks for auto-repair behavior. +`omni_asset_validate --help` may be used to confirm a runtime exists. Do **not** +use the CLI to select which validators run: CLI `--rule` flags take bare names, +which cannot disambiguate the Scene Optimizer and Asset Validator rules that +share a class name. Concept selection and execution always go through the +canonical executor (`scripts/usd_validation_executor.py`), which resolves by +identity. Prefer CSV when JSON output is not advertised by the selected runtime. -## Standalone Python API Pattern +## Kit API Pattern -Use the Python API from the selected standalone environment when structured -issue extraction is needed: +Inside Kit, start Kit with the validation extension enabled, then run via the +executor — do not hand-roll engine setup or enable rules by name: ```python -from omni.asset_validator import ValidationEngine - -engine = ValidationEngine() -results = engine.validate("asset.usda") -issues = results.issues() if callable(results.issues) else results.issues +from usd_validation_executor import validate_concepts, run_scope_note ``` -## Masked-Stage Spot Check Pattern +`validate_concepts` / `run_scope_note` import `omni.asset_validator.core`, +construct the engine with `init_rules=False`, and enable only the resolved rule +classes for the scope note's canonical concepts. Do not construct the engine +with default/all-rule initialization unless exhaustive validation was explicitly +approved. -Policy, sample selection rules, and implementation pattern live in -`validation-scoping.md` → *Masked-Stage Spot Checks*. Use that -reference for the canonical `Usd.Stage.OpenMasked()` pattern, mesh coverage -floor (≥25%), and sample manifest fields. +## Standalone API Pattern -Inside Kit, use `omni.asset_validator.core.ValidationEngine` instead of the -standalone import. For a live Kit stage, pass both root and session layers: +In standalone environments, use the same executor entry points. It imports +`omni.asset_validator.core`, falling back to `omni.asset_validator` if needed, +and fails closed with `ValidationRuntimeUnavailable` when neither is importable. +Concepts come from the scope note; never enable rules by bare name. -```python -default_prim = original_stage.GetDefaultPrim() -if default_prim: - mask.Add(default_prim.GetPath()) -masked_stage = Usd.Stage.OpenMasked(original_stage.GetRootLayer(), original_stage.GetSessionLayer(), mask) -if default_prim and not masked_stage.GetDefaultPrim().IsValid(): - raise RuntimeError(f"masked stage excluded default prim {default_prim.GetPath()}") -``` +## Masks And Load Behavior -## Large Report Summarization - -Apply this policy before reading any Asset Validator JSON, CSV, stdout, or log -artifact into agent context. - -First inspect artifact size (`wc -c` on POSIX, `Get-Item | Select -Length` in PowerShell). Treat the artifact as large when it is over 5 MB, when -it has more than 10,000 CSV rows, or when it was produced by a full-stage pass -over an unknown or customer-scale asset. A 166 MB validation report is always a -large artifact. - -For large artifacts, keep the raw report on disk and create or use a compact -summary first: - -- If a packaged summarizer exists for the selected runtime, run it and read - only its compact JSON output. -- If no summarizer exists, create a temporary stdlib-only script beside the - artifact (for example `/_summarize_asset_validator_report.py`). - The script is a run artifact, not repository content, unless the user - explicitly asks to add tooling. -- For CSV reports, stream rows with Python's `csv.DictReader`; do not load the - whole file or print raw rows. -- For JSON reports, parse in the helper process and emit compact counts. It is - acceptable for the helper process to load JSON if the runtime has enough - memory, because the raw JSON still never enters agent context. If the JSON is - too large or the shape is unknown, emit top-level keys and an error summary, - then ask for a CSV export or narrower rule/category run. - -The compact summary should include: - -- `report_path` and `report_bytes` -- `issue_counts` by severity -- top rules/categories by issue count -- grouped failures by `(rule, severity, message, suggestion)` -- up to 10 example locations per group -- `truncated: true|false` -- parse errors or skipped records, if any - -Read only that compact summary into context. The final user-facing validation -report may include capped examples and artifact paths, but it must not include -the full raw `issues` list when the raw report is large. - -## Categories - -Common categories include: - -- `Basic` -- `Geometry` -- `Layer` -- `Layout` -- `Material` -- `Physics` -- `Other` +When the scope note calls for a representative spot check, use target files or +`Usd.Stage.OpenMasked()`. Preserve the default prim, include material or +relationship closure paths when material rules are selected, and verify the +masked stage still exposes relevant mesh-bearing content. -## Output Report +Do not rely on `LoadNone` as the validator scoping mechanism. See +`../README.md` → `Asset Validator Load Rules`. -Reports should include: - -- `asset_path` -- `validator_skill` -- `validator_tool` -- `passed` -- `categories` -- `rules` -- `issue_counts` -- `issues` -- `timeout_seconds` -- `timeout_reason` -- `warnings` -- `errors` -- `next_step` +## Output Report -## Pass/Fail Policy +Record: -Fail when: +- provider, version, command/API path, and runtime path +- scope, selected rules, target paths, masks, and approvals +- raw artifact paths and compact summary paths +- issue counts grouped by severity and rule; include provider category only as + lookup metadata when the runtime emits it +- failures, warnings, skipped checks, timeouts, and limitations -- the Asset Validator dependency is missing -- the asset cannot be opened by Asset Validator -- any issue has severity `ERROR` or `FAILURE` +Do not paste complete validator rows into the user-facing report. -Warn when: +## Pass/Fail Policy -- issues have severity `WARNING` -- the selected category or rule set is narrower than the requested validation goal -- auto-fix suggestions exist but were not applied +Fail only for tool/runtime failure, unreadable stage, schema violation, or +explicit conformance failure. Performance opportunities are findings, not +command failures. ## Limitations -- This validation reference reports Asset Validator results only; it does not apply `--fix` or repair USD content unless the user explicitly asks for auto-repair behavior. -- Rule coverage depends on the selected Kit or standalone Asset Validator version and installed categories. -- Scene Optimizer performance validators are separate and should run through `so-run-validators` when setup verifies `omni.scene.optimizer.core`. -- Remote `omniverse://` targets require an authenticated Kit runtime; standalone validation is for local paths. -- Masked-stage spot checks are optimization evidence, not a substitute for a - full conformance pass when the user asks for formal validation coverage. - -### Payload Loading Behavior - -The Asset Validator discards `StageLoadRules` and re-opens with `LoadAll` — -all payloads are loaded regardless of caller configuration. `StagePopulationMask` -IS preserved (`OpenMasked` works for scoping). Do not rely on `LoadNone` for -validation scoping; use `OpenMasked` or validate standalone files. - -See `validation-scoping.md` §"LoadRules Behavior With the Asset Validator" for -full consequences and workarounds. +- CLI flags and Python APIs vary by installed runtime/version. +- This reference reports Asset Validator findings only; it does not apply + `--fix` or repair USD content unless the user explicitly asks for auto-repair. +- Scene Optimizer performance validators run through `so-run-validators` when + setup verifies `omni.scene.optimizer.core`. +- Spot checks are optimization evidence, not formal full conformance coverage. ## Troubleshooting -- If neither runtime path is available, run `setup-usd-performance-tuning` again or use `install-asset-validator-standalone` to create a project-managed standalone validator. -- If the SO bundled validator venv is missing dependencies such as `numpy`, select the Kit path or project-managed standalone path and record any fallback limitation. -- If validation times out on a large stage, keep the partial report/log, narrow the scope, or ask the user for an explicit larger time budget. -- If Asset Validator passes but external profile/package conformance is still - needed, hand off to that owning external workflow and keep this package's - report focused on USD/Asset Validator evidence. +- If imports fail, return to setup and select or install a supported runtime. +- If the CLI lacks a desired output flag, use an advertised format. +- If validation stalls, stop at the approved budget, keep partial artifacts, and + narrow the next scope through the runner. ## Next Steps -Use this handoff: - -| Asset intent | Next skill | -|---|---| -| Performance validation needed | `so-run-validators` | -| Findings need operation recommendations | `so-interpret-validators` | -| External profile/package conformance needed | Owning external validation workflow | +Pass compact findings to `so-interpret-validators`. Revalidate same-or-narrower +after mutation unless the user approves expansion. diff --git a/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/validation-scoping.md b/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/validation-scoping.md deleted file mode 100644 index ac5c0ba6..00000000 --- a/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/validation-scoping.md +++ /dev/null @@ -1,542 +0,0 @@ - - - -# Validation Scoping — Canonical Cost Guardrails - -> **This is the single source of truth** for validation cost control in the -> digitaltwin-performance-skill workflow. Other references -> (`usd-validation-runner`, `validate-usd-asset-validator`, `workflow.md`) -> point here for tier detail, approval gates, and scoping rules rather than -> duplicating them. - ---- - -## Decision Tree (read this first) - -``` -┌─ What did structure assessment say? ─────────────────────────────────────┐ -│ │ -│ phase_recommendation = "structuring" │ -│ → Structural validators only (do not validate geometry about to be │ -│ restructured) │ -│ │ -│ phase_recommendation = "optimization" │ -│ → Tier 1: always (full stage) │ -│ → Tier 2: per flagged asset (or 10% sample if budget allows) │ -│ → Tier 3: only on specifically flagged targets, ALWAYS ask first │ -│ │ -│ phase_recommendation = "already_optimized" │ -│ → Tier 1 only; ask user before expanding scope │ -│ │ -│ No structure assessment available │ -│ → Run structure assessment first. Do not begin with validators. │ -│ │ -├─ Is a full-stage Asset Validator sweep requested? ───────────────────────┤ -│ │ -│ Trigger the FULL-SWEEP APPROVAL GATE (§ below) when ANY of: │ -│ • Stage size is unknown │ -│ • Customer-scale factory/CAD/BIM/MEP/plant/city │ -│ • SA reports large prim/mesh/prototype/material counts │ -│ • Request is performance triage (not formal conformance) │ -│ │ -│ Offer: targeted rules (recommended) | full sweep + timeout | defer │ -│ │ -├─ Is full-stage AV too expensive but signal is still needed? ─────────────┤ -│ │ -│ Use MASKED-STAGE SPOT CHECKS (§ below) │ -│ • ≥25% mesh coverage or explain why not │ -│ • Label as sample evidence, not full-stage evidence │ -│ │ -├─ Is a full-stage category sweep planned on a very large mesh count? ─────┤ -│ │ -│ Do NOT run uncapped Basic/Layer/Geometry/etc. over the whole stage. │ -│ Use masked spot checks, or run each selected category/rule in a │ -│ subprocess with an explicit wall-clock budget and report TIMEOUT rows. │ -│ │ -├─ Stage has >30K prototype meshes and per-rule attribution is needed? ────┤ -│ │ -│ Use PER-RULE TIMEOUT pattern (§ below) │ -│ │ -├─ Validating post-decompose targets (payloads, assembly root)? ───────────┤ -│ │ -│ Each target re-enters THIS tree independently (§ Post-Restructure │ -│ Validation Strategy). Treat each payload/assembly as a fresh validation │ -│ request — tier selection, approval gate, and spot-check threshold apply │ -│ based on THAT target's prim count, not the original composed stage. │ -│ │ -└──────────────────────────────────────────────────────────────────────────┘ -``` - ---- - -## Purpose - -Translate structure assessment findings into a concrete validation plan that -avoids running expensive validators on assets that do not need them. Use this -after `usd-structure-assessment` produces its report and before invoking -`so-run-validators`. - -## Inputs - -- Structure assessment report: `flagged_assets`, `validation_scope`, `phase_recommendation`. -- User time/resource budget and approval posture for expensive checks. -- Goal type: diagnosis-only or diagnosis plus execution. -- Validator names available in the installed runtime. - ---- - -## Validator Tiers - -### Tier 1: Fast (seconds per asset) - -Metadata and traversal only. Safe to run on all assets. - -- `printStats` -- `countVertices` -- `rtxMeshCount` -- `removeSmallGeometry` (analysis mode) -- `findFlatHierarchies` (analysis mode) -- `pruneLeaves` (analysis mode) -- `removePrims` (analysis mode — orphaned overs) - -### Tier 2: Medium (seconds to minutes per asset) - -Per-mesh inspection. Run on flagged assets or all if budget allows. - -- `meshCleanup` (analysis mode) -- `optimizeMaterials` (analysis mode) -- `removeUnusedUVs` (analysis mode) -- `computeExtents` (analysis mode) -- `generateNormals` (analysis mode) - -### Tier 3: Expensive (minutes per asset, minutes to hours cross-component) - -Spatial or pairwise analysis. Run only on specifically flagged assets/pairs. -**Always ask the user before running.** - -- `deduplicateGeometry` (analysis mode) — on repetition-flagged assets -- `findCoincidingGeometry` (analysis mode) — on repetition-flagged pairs -- `findOccludedMeshes` (analysis mode) — on containment-flagged pairs -- `fitPrimitives` (analysis mode) — on outlier-flagged or primitive-like assets -- `sparseMeshes` (analysis mode) — on outlier-flagged assets - -### Deferred to Post-Optimization Pass - -These validators are expensive AND their findings do not influence operation -selection — the corresponding operations already run unconditionally based on -structural signals. Running these first adds cost without changing the plan. - -| Validator | Why deferred | -|---|---| -| `optimizePrimvars` / `IndexedPrimvarChecker` | `removeUnusedUVs` + `optimizePrimvars` already run for CAD/BIM. Checker produces 100K+ warnings (~6 min, ~156 MB) confirming what SA already told you. | -| `SceneOptimizerNonManifoldChecker` / `ManifoldChecker` | Per-edge topology; only relevant for simulation targets. Visualization-only workflows skip entirely. | -| `SceneOptimizerUnusedUVsChecker` | Same signal as IndexedPrimvar — the operation is already selected. | - -**Exception:** If the assessment does NOT indicate these operations should run -(e.g., a non-BIM scene where UV removal is uncertain), DO run the validators — -their findings provide the evidence to justify the operation. - ---- - -## Full-Sweep Approval Gate - -Trigger before any command that enables the default AV rule set over the whole -composed stage when ANY condition holds: - -1. Stage size is unknown. -2. Target is customer-scale factory/CAD/BIM/MEP/plant/city. -3. SA reports large prim/mesh/prototype/material counts, many - payloads/references, or a monolithic root layer. -4. Request is performance triage rather than formal conformance. - -**Offer the user three options:** - -- **Recommended:** minimum-openability + targeted category/rule checks. -- **Full sweep:** default AV rule set with explicit timeout and artifact dir. -- **Defer:** skip AV until after SA or after mutation. - -When approved, record `scope: "full_stage"`, `approved_by_user: true`, -`timeout_seconds`, and artifact paths. If not approved, do not launch. - ---- - -## Scoping Rules - -1. **Structure assessment is the first filter.** Use `summary_counts` plus - duplicate-hierarchy candidates to decide which validators can change the - optimization plan. Do not start with a full default AV sweep. - -2. **Start with cheap, optimization-oriented evidence.** Duplicate - geometry/materials, empty leaves, flat hierarchies, high vertex/mesh count - summaries, unused UVs (when tolerant of noise), small/zero-extent geometry - (when scoped). - -3. **Keep SO analysis in the validation workflow.** Use AV-registered SO - checkers when available; otherwise run equivalent read-only SO analysis via - `so-run-validators` in the same plan. Do not treat it as a separate phase. - -4. **Run Tier 2 per-asset** on: - - All assets flagged by SA (any reason). - - A sample of unflagged assets if budget allows (e.g., 10% random). - -5. **Run Tier 3 only on flagged targets:** - - `deduplicateGeometry`: only `repetition`-flagged. - - `findOccludedMeshes`: only `containment`-flagged pairs. - - `fitPrimitives` / `sparseMeshes`: only `outlier_extent` or - primitive-like. For CAD/BIM/MEP, pipe/duct/conduit/fitting names or high - prototype mesh counts are enough evidence. Heavy existing instancing does - not remove this signal. - -6. **Cross-component validators** require the composed stage with relevant - payloads visible. Use `Usd.Stage.OpenMasked()` with a population mask - covering only the flagged pair (and their dependency closures), OR run - the validator via standalone `Usd.Stage.Open()` on each target file. - Do NOT rely on `LoadNone` + selective `stage.Load()` — the Asset Validator - discards load rules and re-opens with LoadAll (see §"LoadRules Behavior - With the Asset Validator" below). - -7. **If no assets are flagged**: Tier 1 only. Report the scene as likely - well-structured. Ask before running Tier 2-3. - -8. **Defer noisy/slow full-stage rules.** Do not run - `IndexedPrimvarChecker`, `CoincidingGeometryChecker`, `ExtentsChecker`, - `AlmostExtremeExtentChecker`, `ArticulationChecker`, `ColliderChecker`, or - `KindChecker` globally in the preliminary pass. Run them only when the - scope makes their output actionable (physics checks for simulation assets, - coinciding geometry for repetition-suspect groups, etc.). - -9. **Cap selected category sweeps on large stages.** A category-scoped AV run - is still a full-stage traversal for that category. If - `summary_counts.mesh_count` or prototype/proxy mesh contribution is very - large, do not launch uncapped `Basic`, `Layer`, `Geometry`, `Material`, or - multi-category sweeps over the full composed stage. Prefer masked-stage spot - checks. If a category sweep is still needed, run each selected category or - rule in its own subprocess with an explicit wall-clock budget, record - `timeout_seconds` / `timeout_reason`, and treat partial results as scoped - evidence rather than full conformance. - -10. **Prefer summaries over issue dumps.** Summarize findings into target - counts and operation candidates. Apply `runtime-artifact-token-budget.md` - for CSV/log/summary handling. - ---- - -## Scene-Aware Tier Adjustment - -The static tier assignments above are defaults. Adjust based on -`phase_recommendation` and scene characteristics: - -| Scene signal | Adjustment | -|---|---| -| CAD/BIM/MEP, pipe/duct-heavy, many primitive-like prototypes | Promote `fitPrimitives` analysis into first SO pass (even when heavily instanced — instancing skips mesh dedup, not prototype primitive fitting). | -| Well-structured, artistic | Promote `deduplicateGeometry` + `optimizeMaterials`; demote `fitPrimitives`. | -| Instancing ratio >80% | Skip `deduplicateGeometry`. | -| Scene has 0 extents | Always include `computeExtents`. | -| Enclosed building | Scope `findOccludedMeshes` to within-floor/within-discipline, or skip entirely. | - -When `removeUnusedUVs` and `fitPrimitives` both target the same CAD/BIM -content, interpret `fitPrimitives` candidates against the post-UV-cleanup -state; a pre-cleanup `nonconstPrimvarMeshCount` bucket is still a -primitive-fit opportunity. - ---- - -## Masked-Stage Spot Checks - -> **See also:** "Post-Restructure Validation Strategy" below for how to validate -> after `apply-restructure` or `decompose-for-selective-loading`. Masked-stage -> techniques apply within that framework when individual targets (assembly skeleton -> or a single large payload) are themselves too expensive for a full sweep. - -Use when full-stage AV is too expensive but prim-level findings can still -change the optimization plan. This is the preferred alternative to dropping -validation entirely on customer-scale assets. - -### When to use - -- Stage is customer-scale (CAD/BIM/MEP/factory/city, tens of thousands of - prototype meshes). -- SA can identify representative candidate subtrees. -- Rule set is mostly `CheckPrim`-style geometry/material/schema checks. -- Result is optimization evidence, not formal conformance. - -### Sample selection - -1. Build a cheap full-stage inventory first: top branches by mesh count, - semantic names, top prototype/fingerprint groups, material-heavy branches, - instance-heavy branches. -2. Include all SA-flagged targets that may change the operation plan. -3. Cover **≥25% of mesh-bearing content** by mesh count, `rtxMeshCount`, or - instance-proxy mesh contribution. If floor is impractical, record why and - mark result as limited sample evidence. -4. Include high-risk exemplars: largest mesh, deepest hierarchy, - material-heavy mesh, repeated module, top prototype/fingerprint family, - dominant mesh-bearing semantic classes (pipe/duct/conduit/fitting for MEP; - equivalent for other scenes). -5. Do not sample bare generated prototype roots unless the masked stage is - verified to expose descendant meshes. - -### Implementation - -```python -from pxr import Sdf, Usd, UsdGeom -from omni.asset_validator import CategoryRuleRegistry, ValidationEngine - -asset_abs = "/path/to/customer-scale.usd" -root_layer = Sdf.Layer.FindOrOpen(asset_abs) -default_prim_path = f"/{root_layer.defaultPrim}" if root_layer.defaultPrim else None - -sampled_paths = ["/Root/Prototypes/PipeFamily_A", "/Root/Mechanical/Level_02/AHU_01"] -closure_paths = ["/Root/Looks", "/Root/Materials"] - -mask = Usd.StagePopulationMask() -for path in [*sampled_paths, *closure_paths, default_prim_path]: - if path: - mask.Add(path) - -stage = Usd.Stage.OpenMasked(root_layer, mask) -assert stage.GetDefaultPrim().IsValid(), "masked stage excluded default prim" - -# Verify mesh coverage (proxy/prototype-aware) -mesh_count = sum( - 1 for prim in Usd.PrimRange.Stage( - stage, Usd.TraverseInstanceProxies(Usd.PrimDefaultPredicate) - ) if prim.IsA(UsdGeom.Mesh) -) -# Must be ≥25% of total_scene_mesh_count; raise if 0 - -registry = CategoryRuleRegistry() -normals_rule = next( - rule for rule in registry.get_rules("Geometry") if rule.__name__ == "NormalsValidChecker" -) - -engine = ValidationEngine(init_rules=False) -engine.enable_rule(normals_rule) -results = engine.validate(stage) -``` - -### Constraints - -- Preserve default prim in the mask. -- Add material scopes / relationship targets when running material rules. -- Avoid stage-global / layer-global / dependency rules in spot mode. -- Label output `scope: "masked_stage_spot_check"` with sampled paths, - semantic tags, mesh coverage %, and evidence scope. -- Reject empty samples: if proxy/prototype-aware counts report 0 meshes, - resample instead of reporting "0 findings." - ---- - -## Post-Restructure / Post-Decompose Validation Strategy - -After `apply-restructure` (mode=restructure) or `decompose-for-selective-loading` -produces an assembly root + N payload/prototype files, do NOT open the full composed -stage with all payloads loaded for a blanket validator sweep. That re-couples what was -just decomposed and doubles validation work. - -### Validation Distribution - -- **Assembly skeleton** — open with `Usd.Stage.OpenMasked()` excluding payload prim - paths. Run structural validators only: reference resolution, kind hierarchy, layer - structure, defaultPrim, extent hints, assetInfo. Single pass. - -- **Assembly root as optimization target** — if the assembly root retains mesh - content after extraction (ground planes, shared environment geometry, - non-extracted sub-hierarchies), it is itself a Phase 4 target. Validate and - optimize it like any other target via the decision tree. The manifest records - this with `target_class: "assembly_root"`. - -- **Each payload/prototype (standalone)** — open each file independently with - `Usd.Stage.Open(payload_file)`. Plan validation through the decision tree for - each target (tier selection and approval thresholds apply per-target based on - that target's prim count — a small payload may qualify for spot-check rather - than a full sweep). These are independent and can run in parallel. - -- **Cross-payload pairs** — open with `Usd.Stage.OpenMasked(root, mask)` covering - only the two relevant payload subtrees. Run Tier 3 only (findCoincidingGeometry, - etc.) per flagged pair. - -### Rules - -1. Validate each payload as a standalone file. Do not rely on the assembly context - for per-payload geometry validation. -2. Validate payloads in parallel — they are independent by definition. -3. The assembly-skeleton pass confirms composition integrity (all refs resolve, - no dangling payload arcs, kind/model hierarchy correct). -4. Cross-payload Tier 3 validators use `OpenMasked` to expose only the specific - pair, not all payloads. -5. Report results per-target (assembly vs each payload) so the user sees which - specific payload has issues. -6. Each target in the per-target loop re-enters THIS decision tree as if it were - a fresh validation request — the tree's tier selection, spot-check threshold, - and approval gate apply per-target based on that target's prim count. - -### Why NOT re-compose - -Opening the full composed stage with all payloads loaded for validation after -decompose: -- Defeats the purpose of decomposition (cost is the same as the original). -- Produces per-prim findings attributed to composed paths that don't map cleanly - to individual payload files. -- Prevents parallel validation of independent payloads. -- Triggers the same cost gate the user already approved once (double-prompting). - ---- - -## LoadRules Behavior With the Asset Validator - -The Asset Validator's `ComplianceChecker` opens a **new stage** from -the input's root layer with default `LoadAll` semantics. The original stage's -load rules (e.g. `LoadNone`) are discarded. - -`StagePopulationMask` IS preserved — if the input stage was opened with -`Usd.Stage.OpenMasked()`, the mask carries through to the validator's internal stage. - -This is observable behaviour across current AV versions; do not assume future -releases will behave the same. Probe the AV API/version when possible. - -### Consequences - -- Passing a `LoadNone` stage to the AV is equivalent to passing a `LoadAll` stage. -- All payload content will be loaded and traversed regardless of the load predicate. -- `Usd.PrimRange.Stage(stage, Usd.TraverseInstanceProxies())` visits everything loaded. -- Elapsed time and findings will reflect the full composed stage, not the skeleton. - -### What works - -- `Usd.Stage.OpenMasked()` — physically excludes prims from the stage's view. The AV - preserves the population mask, so masked prims remain invisible to checkers. -- Opening each payload file standalone (preferred — see "Validation Distribution" above). - -### What does NOT work - -- `Usd.Stage.Open(root, Usd.Stage.LoadNone)` — load rules are discarded by AV. -- `stage.Load(specific_path)` after `LoadNone` — same problem, AV re-opens from root. -- Any load-state scoping strategy that relies on `StageLoadRules` being preserved. - ---- - -## Per-Rule Timeout Pattern - -For stages with very high mesh/prototype counts where total validation would -exceed the time budget. Run each category or rule in its own subprocess with an -explicit budget so a single slow checker does not consume the whole pass. - -```python -import subprocess, json, sys -from omni.asset_validator import CategoryRuleRegistry, ValidationEngine -from pxr import Usd - -def validate_one_rule(stage_path, category, rule_name, per_rule_budget_s): - registry = CategoryRuleRegistry() - rule = next( - rule for rule in registry.get_rules(category) if rule.__name__ == rule_name - ) - engine = ValidationEngine(init_rules=False) - engine.enable_rule(rule) - stage = Usd.Stage.Open(stage_path) - results = engine.validate(stage) - return list(results.issues()) - -# Driver: -RULES = [ - ("Geometry", "ManifoldChecker"), - ("Geometry", "WeldChecker"), -] -results = {} -for category, rule_name in RULES: - try: - out = subprocess.run( - [sys.executable, __file__, stage_path, category, rule_name], - timeout=per_rule_budget_s, capture_output=True, check=True, - ) - results[f"{category}.{rule_name}"] = json.loads(out.stdout) - except subprocess.TimeoutExpired: - results[f"{category}.{rule_name}"] = { - "status": "timed-out", - "budget_s": per_rule_budget_s, - } -``` - -**When to use:** >30K prototype meshes, very large `summary_counts.mesh_count`, -suspected single-rule hang, or iterating on one rule without re-running others. -For small/medium stages, use the standard tier plan via `so-run-validators`. - ---- - -## Output — Validation Plan JSON - -```json -{ - "tier1": { - "scope": "full_stage", - "validators": ["printStats", "countVertices", "rtxMeshCount", "..."] - }, - "tier2": { - "scope": "per_asset", - "assets": ["path/to/A.geom.usd", "path/to/B.geom.usd"], - "validators": ["meshCleanup", "optimizePrimvars", "..."] - }, - "tier3": { - "scope": "targeted", - "tasks": [ - { "validator": "findOccludedMeshes", "targets": [["CabinetA", "PipeB"]] }, - { "validator": "deduplicateGeometry", "targets": ["ChairA", "ChairB"] } - ] - }, - "spot_checks": { - "scope": "masked_stage_spot_check", - "sampled_paths": ["", ""], - "mesh_coverage_percent": 28.4, - "evidence_scope": "sample" - }, - "skip": ["list of unflagged assets - no deep validation planned"], - "estimated_time": "fast | minutes | long" -} -``` - -This plan is passed to `so-run-validators` (executor) by `usd-validation-runner` (router). - ---- - -## Hard Rules - -1. Never run all validators on all assets by default. -2. Never run Tier 3 without structural evidence from the assessment. -3. Always ask before Tier 3 cross-component checks. -4. Never start a performance workflow with a full default AV sweep. -5. Prefer masked-stage spot checks over dropping validation when full-stage - is too expensive. -6. Always report what was skipped and why — the user may override. - ---- - -## Duration Hints (typical: ~100K prims, ~200K meshes) - -| Scope | Expected | -|---|---| -| Tier 1 (full stage) | ~5 min | -| Tier 2 (per flagged asset) | ~30 min | -| Tier 3 (cross-component) | hours — always confirm | -| Structural validators only | ~2 min | -| Masked-stage spot check | ~5-10 min | - ---- - -## Prerequisites - -- Structure assessment report (specifically `flagged_assets` and `validation_scope`). -- User time/resource budget. -- Access to validator names in the installed runtime. - -## Limitations - -- Scope quality depends on the structure assessment; weak evidence stays visible. -- Tier cost estimates are defaults — they scale with scene size. -- SO validator names must match the installed runtime. - -## Troubleshooting - -- No assets flagged → Tier 1 only, ask before expanding. -- Tier 3 requested without evidence → return to `usd-structure-assessment`. -- Named validator unavailable → record gap, choose nearest supported source. diff --git a/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/validator-concepts.json b/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/validator-concepts.json new file mode 100644 index 00000000..3817c7cb --- /dev/null +++ b/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/references/validator-concepts.json @@ -0,0 +1,950 @@ +{ + "schema_version": "1.0.0", + "$comment": "Canonical validator-concept registry for the USD performance-tuning workflow. Single binding layer between agent-facing concept names and runtime rule identity, tier, scope policy, and backing op. Curated by maintainers, not inferred. Resolution key is (module, class_name); category is informational. Tiers/costs are CPU-only worst-case from gb300_03 evidence (batch-additivity-findings.md); gpu_bound concepts relax on CUDA hosts. Governed by validator-concepts.schema.json (added in PR-2).", + "concepts": [ + { + "canonical_name": "primvar_indexability", + "display_name": "Indexed primvars", + "role": "opportunity_detector", + "backing_op": "optimizePrimvars", + "tier": 2, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "per_target_or_sample", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.indexed_primvars_checker", + "class_name": "IndexedPrimvarChecker", + "category": "Omni:Geometry", + "use_for": ["performance_tuning"] + }, + { + "provider": "oav", + "module": "omni.asset_validator._geometry_checker", + "class_name": "IndexedPrimvarChecker", + "category": "Geometry", + "tier": 3, + "use_for": ["conformance_audit"] + } + ], + "notes": "Name collision: SO triage wrapper (0.3 s) vs OAV full audit (376 s). Resolve by module; SO is the perf-tuning default." + }, + { + "canonical_name": "primvar_unused", + "display_name": "Unused UV/primvar sets", + "role": "opportunity_detector", + "backing_op": "removeUnusedUVs", + "tier": 2, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "per_target_or_sample", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.unused_uvs_checker", + "class_name": "UnusedUVsChecker", + "category": "Usd:Performance", + "use_for": ["performance_tuning"] + }, + { + "provider": "oav", + "module": "omni.asset_validator._geometry_checker", + "class_name": "UnusedPrimvarChecker", + "category": "Geometry", + "use_for": ["conformance_audit"] + } + ], + "notes": "SO is UV-scoped and ties to removeUnusedUVs; OAV covers all primvars." + }, + { + "canonical_name": "vertex_weld", + "display_name": "Colocated vertices", + "role": "opportunity_detector", + "backing_op": "meshCleanup", + "tier": 2, + "cost_class": "medium", + "gpu_bound": false, + "scope_policy": "per_target_or_sample", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.colocated_vertices_checker", + "class_name": "ColocatedVerticesChecker", + "category": "Omni:Geometry", + "use_for": ["performance_tuning"] + }, + { + "provider": "oav", + "module": "omni.asset_validator._geometry_checker", + "class_name": "WeldChecker", + "category": "Geometry", + "use_for": ["conformance_audit"] + } + ] + }, + { + "canonical_name": "topology_zero_area_faces", + "display_name": "Zero-area faces", + "role": "opportunity_detector", + "backing_op": "meshCleanup", + "tier": 2, + "cost_class": "medium", + "gpu_bound": false, + "scope_policy": "per_target_or_sample", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.zero_area_faces_checker", + "class_name": "ZeroAreaFacesChecker", + "category": "Omni:Geometry", + "use_for": ["performance_tuning"] + }, + { + "provider": "oav", + "module": "omni.asset_validator._geometry_checker", + "class_name": "ZeroAreaFaceChecker", + "category": "Geometry", + "use_for": ["conformance_audit"] + } + ], + "notes": "Class-name singular (OAV) vs plural (SO)." + }, + { + "canonical_name": "topology_manifold", + "display_name": "Non-manifold topology", + "role": "opportunity_detector", + "backing_op": "meshCleanup", + "tier": 3, + "cost_class": "stage_dependent", + "gpu_bound": false, + "scope_policy": "per_target_or_sample", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.nonmanifold_checker", + "class_name": "NonManifoldChecker", + "category": "Omni:Geometry", + "use_for": ["performance_tuning"] + }, + { + "provider": "oav", + "module": "omni.asset_validator._geometry_checker", + "class_name": "ManifoldChecker", + "category": "Geometry", + "use_for": ["conformance_audit"] + } + ], + "notes": "Skip for visualization-only targets; run only for simulation-ready intent. Inverse polarity between impls. Measure before promoting off Tier 3." + }, + { + "canonical_name": "normals_validity", + "display_name": "Missing/invalid normals", + "role": "opportunity_detector", + "backing_op": "generateNormals", + "tier": 3, + "cost_class": "medium", + "gpu_bound": false, + "scope_policy": "per_target_or_sample", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.normals_checker", + "class_name": "NormalsChecker", + "category": "Usd:Performance", + "use_for": ["performance_tuning"] + }, + { + "provider": "oav", + "module": "omni.asset_validator._geometry_checker", + "class_name": "NormalsValidChecker", + "category": "Geometry", + "use_for": ["conformance_audit"] + } + ], + "notes": "Run as Tier 3 only when normals quality is relevant enough to ask for a targeted check." + }, + { + "canonical_name": "normals_winding", + "display_name": "Inconsistent face winding", + "role": "opportunity_detector", + "backing_op": "meshCleanup", + "tier": 2, + "cost_class": "medium", + "gpu_bound": false, + "scope_policy": "per_target_or_sample", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.windings_checker", + "class_name": "WindingsChecker", + "category": "Usd:Performance", + "use_for": ["performance_tuning"] + }, + { + "provider": "oav", + "module": "omni.asset_validator._geometry_checker", + "class_name": "NormalsWindingsChecker", + "category": "Geometry", + "use_for": ["conformance_audit"] + } + ] + }, + { + "canonical_name": "primitive_fit", + "display_name": "Primitive-fittable meshes", + "role": "opportunity_detector", + "backing_op": "fitPrimitives", + "tier": 2, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "per_target_or_sample", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.primitive_fit_checker", + "class_name": "PrimitiveFitChecker", + "use_for": ["performance_tuning"] + } + ], + "parameter_prerequisite": { + "op": "fitPrimitives", + "param": "tolerance", + "question": "What primitive-fit tolerance is acceptable (max deviation from the original mesh)?" + }, + "notes": "Mandatory default opportunity_detector for CAD/BIM/MEP optimization posture. Highest-value reduction for HOOPS-tessellated converted content. Bounded-loss op gated by tolerance." + }, + { + "canonical_name": "geom_duplicates", + "display_name": "Duplicate geometry (exact)", + "role": "opportunity_detector", + "backing_op": "deduplicateGeometry", + "tier": 3, + "cost_class": "stage_dependent", + "gpu_bound": false, + "scope_policy": "per_target_or_sample", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.duplicate_geometry_checker", + "class_name": "DuplicateGeometryChecker", + "category": "Usd:Performance", + "use_for": ["performance_tuning"] + } + ], + "notes": "2.5 s on gb300 (hash short-circuit) but can be minutes with many similar meshes. Treat as Tier 3 default; promote to Tier 2 only after measuring on target stage." + }, + { + "canonical_name": "geom_duplicates_fuzzy", + "display_name": "Duplicate geometry (fuzzy)", + "role": "opportunity_detector", + "backing_op": "deduplicateGeometry", + "tier": 3, + "cost_class": "stage_dependent", + "gpu_bound": false, + "scope_policy": "per_target_or_sample", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.duplicate_geometry_fuzzy_checker", + "class_name": "FuzzyDuplicateGeometryChecker", + "category": "Usd:Performance", + "use_for": ["performance_tuning"] + } + ], + "notes": "Point-cloud distance comparison; can scale poorly. Same backing op, different threshold." + }, + { + "canonical_name": "material_duplicates", + "display_name": "Duplicate materials", + "role": "opportunity_detector", + "backing_op": "optimizeMaterials", + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.duplicate_materials_checker", + "class_name": "DuplicateMaterialsChecker", + "use_for": ["performance_tuning"] + } + ], + "notes": "Material-network hash. High value on converted content where materials are duplicated per instance." + }, + { + "canonical_name": "topology_duplicate_faces", + "display_name": "Duplicate faces", + "role": "opportunity_detector", + "backing_op": "meshCleanup", + "tier": 2, + "cost_class": "medium", + "gpu_bound": false, + "scope_policy": "per_target_or_sample", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.duplicate_face_checker", + "class_name": "DuplicateFaceChecker", + "use_for": ["performance_tuning"] + } + ] + }, + { + "canonical_name": "topology_isolated_vertices", + "display_name": "Isolated vertices", + "role": "opportunity_detector", + "backing_op": "meshCleanup", + "tier": 2, + "cost_class": "medium", + "gpu_bound": false, + "scope_policy": "per_target_or_sample", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.isolated_vertices_checker", + "class_name": "IsolatedVerticesChecker", + "use_for": ["performance_tuning"] + } + ] + }, + { + "canonical_name": "extents_zero", + "display_name": "Zero-extent meshes", + "role": "opportunity_detector", + "backing_op": "removeSmallGeometry", + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.zero_extent_checker", + "class_name": "ZeroExtentChecker", + "use_for": ["performance_tuning"] + } + ], + "notes": "Use computeExtents first when the cause is stale metadata rather than genuinely empty geometry." + }, + { + "canonical_name": "perf_rtx_mesh_count", + "display_name": "RTX mesh count", + "role": "opportunity_detector", + "backing_op": "rtxMeshCount", + "tier": 1, + "cost_class": "medium", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.rtx_mesh_count_checker", + "class_name": "RtxMeshCountChecker", + "use_for": ["performance_tuning"] + } + ], + "notes": "~94 s on gb300 — slowest Tier 1. Counts meshes through composition." + }, + { + "canonical_name": "perf_small_mesh", + "display_name": "Small meshes", + "role": "opportunity_detector", + "backing_op": "removeSmallGeometry", + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.small_mesh_checker", + "class_name": "SmallMeshChecker", + "use_for": ["performance_tuning"] + } + ] + }, + { + "canonical_name": "perf_sparse_mesh", + "display_name": "Sparse meshes", + "role": "opportunity_detector", + "backing_op": "sparseMeshes", + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.sparse_mesh_checker", + "class_name": "SparseMeshChecker", + "use_for": ["performance_tuning"] + } + ] + }, + { + "canonical_name": "perf_high_vertex_count", + "display_name": "High vertex count", + "role": "opportunity_detector", + "backing_op": "countVertices", + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.high_vertex_count_checker", + "class_name": "HighVertexCountChecker", + "use_for": ["performance_tuning"] + } + ], + "notes": "Informational; lossless reducers first, decimateMeshes only after the upfront tolerance prompt." + }, + { + "canonical_name": "perf_redundant_timesamples", + "display_name": "Redundant time samples", + "role": "opportunity_detector", + "backing_op": "optimizeTimeSamples", + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.redundant_timesamples_checker", + "class_name": "RedundantTimeSamplesChecker", + "use_for": ["performance_tuning"] + } + ] + }, + { + "canonical_name": "structure_empty_leaf", + "display_name": "Empty leaf prims", + "role": "opportunity_detector", + "backing_op": "pruneLeaves", + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.empty_leaf_checker", + "class_name": "EmptyLeafChecker", + "use_for": ["performance_tuning"] + } + ] + }, + { + "canonical_name": "structure_flat_hierarchy", + "display_name": "Flat hierarchies", + "role": "target_scoping", + "backing_op": "flattenHierarchy", + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.flat_hierarchies_checker", + "class_name": "FlatHierarchiesChecker", + "use_for": ["performance_tuning"] + } + ], + "notes": "Analysis-only signal; fix is the flattenHierarchy operation (specialty follow-up)." + }, + { + "canonical_name": "structure_invisible", + "display_name": "Invisible prims", + "role": "opportunity_detector", + "backing_op": "removePrims", + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.invisible_prims_checker", + "class_name": "InvisiblePrimsChecker", + "use_for": ["performance_tuning"] + } + ], + "notes": "Confirm intent before removing; invisibility may be deliberate." + }, + { + "canonical_name": "spatial_occluded", + "display_name": "Occluded (internal) meshes", + "role": "opportunity_detector", + "backing_op": "findOccludedMeshes", + "tier": 3, + "cost_class": "expensive", + "gpu_bound": true, + "scope_policy": "flagged_pairs_only", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.occluded_meshes_checker", + "class_name": "OccludedMeshesChecker", + "use_for": ["performance_tuning"] + } + ], + "notes": "Two-step detect->act: feed occluded paths to removePrims. Scope to SA containment pairs with enclosure_opaque:true. 485 s full-stage on CPU; scope via paths=. Two-stage approval (analysis cost, then deletion)." + }, + { + "canonical_name": "spatial_overlapping", + "display_name": "Overlapping meshes", + "role": "target_scoping", + "backing_op": "findOverlappingMeshes", + "tier": 3, + "cost_class": "expensive", + "gpu_bound": true, + "scope_policy": "flagged_pairs_only", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.find_overlapping_meshes_checker", + "class_name": "FindOverlappingMeshesChecker", + "use_for": ["performance_tuning"] + } + ], + "notes": ">3,600 s full-stage on CPU; paths=-scoped to 100 meshes = 72 s. Analysis-only; fix is review/merge. Mandatory scoped probe when SA flags routing pairs." + }, + { + "canonical_name": "spatial_coinciding", + "display_name": "Coinciding geometry", + "role": "target_scoping", + "backing_op": "findCoincidingGeometry", + "tier": 3, + "cost_class": "expensive", + "gpu_bound": true, + "scope_policy": "flagged_pairs_only", + "preferred_provider": "so", + "implementations": [ + { + "provider": "so", + "module": "omni.scene.optimizer.validators.coinciding_geometry_checker", + "class_name": "CoincidingGeometryChecker", + "use_for": ["performance_tuning"] + } + ], + "notes": "229 s full-stage on CPU. Prefer deduplicateGeometry before destructive deletion." + }, + { + "canonical_name": "composition_missing_ref", + "display_name": "Missing references", + "role": "safety_gate", + "backing_op": null, + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._base_rules", + "class_name": "MissingReferenceChecker", + "use_for": ["safety_gate", "conformance_audit"] + } + ], + "notes": "Manual fix. Common on converted assets with absolute paths. High-priority safety gate for CAD/BIM conversions." + }, + { + "canonical_name": "extents_general", + "display_name": "Extents conformance", + "role": "opportunity_detector", + "backing_op": "computeExtents", + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._base_rules", + "class_name": "ExtentsChecker", + "use_for": ["performance_tuning", "conformance_audit"] + } + ], + "notes": "Broader than SO ZeroExtentChecker." + }, + { + "canonical_name": "kind_metadata", + "display_name": "Kind metadata", + "role": "safety_gate", + "backing_op": null, + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._base_rules", + "class_name": "KindChecker", + "use_for": ["safety_gate", "conformance_audit"] + } + ], + "notes": "Manual fix via USD API." + }, + { + "canonical_name": "type_metadata", + "display_name": "Prim type metadata", + "role": "safety_gate", + "backing_op": null, + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._base_rules", + "class_name": "TypeChecker", + "use_for": ["safety_gate", "conformance_audit"] + } + ] + }, + { + "canonical_name": "stage_metadata", + "display_name": "Stage metadata", + "role": "safety_gate", + "backing_op": null, + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._base_rules", + "class_name": "StageMetadataChecker", + "use_for": ["safety_gate", "conformance_audit"] + } + ] + }, + { + "canonical_name": "prim_encapsulation", + "display_name": "Prim encapsulation", + "role": "safety_gate", + "backing_op": null, + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._base_rules", + "class_name": "PrimEncapsulationChecker", + "use_for": ["safety_gate", "conformance_audit"] + } + ] + }, + { + "canonical_name": "layout_default_prim", + "display_name": "Default prim", + "role": "safety_gate", + "backing_op": null, + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._layout_checker", + "class_name": "DefaultPrimChecker", + "use_for": ["safety_gate", "conformance_audit"] + } + ] + }, + { + "canonical_name": "layout_dangling_over", + "display_name": "Dangling over prims", + "role": "safety_gate", + "backing_op": null, + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._layout_checker", + "class_name": "DanglingOverPrimChecker", + "use_for": ["safety_gate", "conformance_audit"] + } + ] + }, + { + "canonical_name": "material_path", + "display_name": "Material asset paths", + "role": "safety_gate", + "backing_op": null, + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._material_checker", + "class_name": "MaterialPathChecker", + "use_for": ["safety_gate", "conformance_audit"] + } + ], + "notes": "info:mdl:sourceAsset pointing at missing files. Common on converted assets." + }, + { + "canonical_name": "material_dangling_binding", + "display_name": "Dangling material bindings", + "role": "safety_gate", + "backing_op": null, + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._material_checker", + "class_name": "UsdDanglingMaterialBinding", + "use_for": ["safety_gate", "conformance_audit"] + } + ] + }, + { + "canonical_name": "texture_bind", + "display_name": "Texture asset paths", + "role": "safety_gate", + "backing_op": null, + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._base_rules", + "class_name": "TextureChecker", + "use_for": ["safety_gate", "conformance_audit"] + } + ] + }, + { + "canonical_name": "texture_normalmap", + "display_name": "Normal-map textures", + "role": "safety_gate", + "backing_op": null, + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._base_rules", + "class_name": "NormalMapTextureChecker", + "use_for": ["safety_gate", "conformance_audit"] + } + ] + }, + { + "canonical_name": "layer_spec_health", + "display_name": "Layer spec health", + "role": "safety_gate", + "backing_op": null, + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._layer_checker", + "class_name": "LayerSpecChecker", + "use_for": ["safety_gate", "conformance_audit"] + } + ] + }, + { + "canonical_name": "layer_format_perf", + "display_name": "ASCII layer performance", + "role": "opportunity_detector", + "backing_op": null, + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._layer_checker", + "class_name": "UsdAsciiPerformanceChecker", + "use_for": ["performance_tuning", "conformance_audit"] + } + ], + "notes": "Flags .usda data layers that should be binary for load performance. Manual/structural fix." + }, + { + "canonical_name": "utf8_paths", + "display_name": "UTF-8 prim names", + "role": "safety_gate", + "backing_op": null, + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._utf8_checker", + "class_name": "UnicodeNameChecker", + "use_for": ["safety_gate", "conformance_audit"] + } + ] + }, + { + "canonical_name": "subdivision_scheme", + "display_name": "Subdivision scheme", + "role": "safety_gate", + "backing_op": null, + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._geometry_checker", + "class_name": "SubdivisionSchemeChecker", + "use_for": ["safety_gate", "conformance_audit"] + } + ] + }, + { + "canonical_name": "topology_general", + "display_name": "Topology validity", + "role": "safety_gate", + "backing_op": null, + "tier": 2, + "cost_class": "medium", + "gpu_bound": false, + "scope_policy": "per_target_or_sample", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._geometry_checker", + "class_name": "ValidateTopologyChecker", + "use_for": ["conformance_audit"] + } + ] + }, + { + "canonical_name": "topology_unused_mesh", + "display_name": "Unused mesh points", + "role": "opportunity_detector", + "backing_op": "meshCleanup", + "tier": 2, + "cost_class": "medium", + "gpu_bound": false, + "scope_policy": "per_target_or_sample", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._geometry_checker", + "class_name": "UnusedMeshTopologyChecker", + "category": "Geometry", + "use_for": ["conformance_audit"] + } + ], + "notes": "meshCleanup removes unreferenced points." + }, + { + "canonical_name": "normals_existence", + "display_name": "Normals existence", + "role": "opportunity_detector", + "backing_op": "generateNormals", + "tier": 2, + "cost_class": "medium", + "gpu_bound": false, + "scope_policy": "per_target_or_sample", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._geometry_checker", + "class_name": "NormalsExistChecker", + "use_for": ["conformance_audit"] + } + ] + }, + { + "canonical_name": "physics_rigid_body", + "display_name": "Rigid body schema", + "role": "safety_gate", + "backing_op": null, + "tier": 1, + "cost_class": "cheap", + "gpu_bound": false, + "scope_policy": "whole_stage", + "preferred_provider": "oav", + "implementations": [ + { + "provider": "oav", + "module": "omni.asset_validator._physics_checker", + "class_name": "RigidBodyChecker", + "use_for": ["conformance_audit"] + } + ], + "notes": "Physics-tagged stages only. Skip for visualization targets. Representative of the physics_* family (collider/joint/articulation/mass) in _physics_checker; class names to be confirmed against the runtime registry before enabling." + } + ] +} diff --git a/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/scripts/asset-validator-validation-report.schema.json b/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/scripts/asset-validator-validation-report.schema.json deleted file mode 100644 index 4ec9a4db..00000000 --- a/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/scripts/asset-validator-validation-report.schema.json +++ /dev/null @@ -1,133 +0,0 @@ -{ - "$comment": "SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\nSPDX-License-Identifier: Apache-2.0", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "additionalProperties": false, - "required": [ - "asset_path", - "validator_skill", - "validator_tool", - "passed", - "status", - "command", - "categories", - "rules", - "issue_counts", - "issues", - "warnings", - "errors", - "next_step" - ], - "properties": { - "asset_path": { - "type": "string" - }, - "validator_skill": { - "const": "validate-usd-asset-validator" - }, - "validator_tool": { - "const": "omni_asset_validate" - }, - "passed": { - "type": "boolean" - }, - "status": { - "type": "string", - "enum": ["PASS", "FAIL", "BLOCKED", "ERROR", "UNKNOWN"] - }, - "command": { - "type": "array", - "items": { - "type": "string" - } - }, - "categories": { - "type": "array", - "items": { - "type": "string" - } - }, - "rules": { - "type": "array", - "items": { - "type": "string" - } - }, - "issue_counts": { - "type": "object", - "additionalProperties": { - "type": "integer", - "minimum": 0 - }, - "required": ["ERROR", "FAILURE", "WARNING", "INFO"], - "properties": { - "ERROR": { - "type": "integer", - "minimum": 0 - }, - "FAILURE": { - "type": "integer", - "minimum": 0 - }, - "WARNING": { - "type": "integer", - "minimum": 0 - }, - "INFO": { - "type": "integer", - "minimum": 0 - } - } - }, - "issues": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "required": [ - "rule", - "severity", - "message", - "location", - "requirement", - "suggestion" - ], - "properties": { - "rule": { - "type": "string" - }, - "severity": { - "type": "string" - }, - "message": { - "type": "string" - }, - "location": { - "type": ["string", "null"] - }, - "requirement": { - "type": ["string", "null"] - }, - "suggestion": { - "type": ["string", "null"] - } - } - } - }, - "warnings": { - "type": "array", - "items": { - "type": "string" - } - }, - "errors": { - "type": "array", - "items": { - "type": "string" - } - }, - "next_step": { - "type": "string" - } - } -} diff --git a/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/scripts/usd_validation_executor.py b/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/scripts/usd_validation_executor.py new file mode 100644 index 00000000..e50bbfe4 --- /dev/null +++ b/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/scripts/usd_validation_executor.py @@ -0,0 +1,577 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Canonical validation reference executor for the USD performance-tuning skill. + +This is the ONE supported way to run validators inside the skill. Agents call +this API with **canonical concept names** (e.g. ``primvar_indexability``); they +never enumerate rules, guess class names, or shell out to a CLI. + +Why this exists +--------------- +Bare rule names are not unique. ``IndexedPrimvarChecker`` is registered by both +Scene Optimizer (0.3 s triage) and the Asset Validator (376 s full audit). A +name-only lookup picks one by registry order, so the same scope note produces +different work and wildly different runtimes on different hosts. That is the +root cause of "every run finds a different solution and it takes forever." + +Contract (no ambiguity, no fallbacks) +------------------------------------- +1. Identity is ``(module, class_name)``, sourced from ``validator-concepts.json``. + Concept -> implementation -> rule class is resolved by identity, never by + bare name. +2. Resolution is fail-closed: zero matches raises, more than one match raises. + The executor never "best-guesses" a rule. +3. The Python validation runtime is required. If it cannot be imported, the + executor raises ``ValidationRuntimeUnavailable`` and the caller records + ``blocked_validation_runtime`` in the coverage ledger. There is no CLI path. +4. Scoping is mandatory for non-whole-stage policies: callers pass ``paths`` / + ``mask_paths`` and the stage is opened with ``Usd.Stage.OpenMasked()``. + +The validator runtime packages (``omni.asset_validator`` / ``pxr``) only import +inside a Kit/AV environment, so every runtime import is deferred into the +function that needs it. Importing this module is always safe (and unit-testable) +without those packages present. +""" +from __future__ import annotations + +import json +import subprocess +from dataclasses import dataclass +from pathlib import Path +from typing import Any, Callable, Iterable + +#: Every ledger disposition is an explicit "resolved" outcome. The completion +#: gate is satisfied only when every planned (target, concept) has one of these. +RESOLVED_STATUSES = frozenset( + { + "probed_with_findings", + "probed_clean", + "user_declined", + "timeout_recorded", + "blocked_validation_runtime", + } +) + +DEFAULT_REGISTRY_PATH = ( + Path(__file__).resolve().parent.parent / "references" / "validator-concepts.json" +) + + +class ConceptResolutionError(RuntimeError): + """A concept name or its identity could not be resolved unambiguously.""" + + +class ValidationRuntimeUnavailable(RuntimeError): + """The Python validation runtime is not importable. No CLI fallback exists.""" + + +#: Single message for every "runtime missing" condition. Callers record +#: ``blocked_validation_runtime`` in the coverage ledger; there is no CLI path. +_RUNTIME_UNAVAILABLE = ( + "No USD validation runtime (omni.asset_validator[.core]) is importable. " + "Record 'blocked_validation_runtime'; there is no CLI fallback." +) + + +@dataclass(frozen=True) +class ResolvedImplementation: + """A concept resolved to a single runtime rule identity.""" + + canonical_name: str + provider: str + module: str + class_name: str + tier: int + scope_policy: str + backing_op: str | None + gpu_bound: bool + + +# --------------------------------------------------------------------------- # +# Registry # +# --------------------------------------------------------------------------- # +def load_registry(path: str | Path | None = None) -> dict[str, Any]: + """Load and index the canonical validator-concept registry. + + Returns a dict with the raw ``concepts`` list plus a ``by_name`` index. + Raises ``ConceptResolutionError`` if a canonical name is duplicated. + """ + registry_path = Path(path) if path is not None else DEFAULT_REGISTRY_PATH + data = json.loads(Path(registry_path).read_text(encoding="utf-8")) + by_name: dict[str, dict[str, Any]] = {} + for concept in data["concepts"]: + name = concept["canonical_name"] + if name in by_name: + raise ConceptResolutionError(f"Duplicate canonical_name in registry: {name}") + by_name[name] = concept + data["by_name"] = by_name + return data + + +def resolve_implementation( + registry: dict[str, Any], + canonical_name: str, + *, + provider: str | None = None, +) -> ResolvedImplementation: + """Resolve a canonical concept name to a single ``(module, class_name)``. + + ``provider`` defaults to the concept's ``preferred_provider`` (``so`` for + performance tuning). Fail-closed: an unknown concept or a provider with no + implementation raises ``ConceptResolutionError``. + """ + concept = registry.get("by_name", {}).get(canonical_name) + if concept is None: + raise ConceptResolutionError( + f"Unknown concept '{canonical_name}'. Concepts must come from " + f"validator-concepts.json; do not synthesize names." + ) + chosen_provider = provider or concept["preferred_provider"] + impls = [im for im in concept["implementations"] if im["provider"] == chosen_provider] + if not impls: + raise ConceptResolutionError( + f"Concept '{canonical_name}' has no '{chosen_provider}' implementation." + ) + if len(impls) > 1: + raise ConceptResolutionError( + f"Concept '{canonical_name}' has ambiguous '{chosen_provider}' implementations." + ) + impl = impls[0] + return ResolvedImplementation( + canonical_name=canonical_name, + provider=impl["provider"], + module=impl["module"], + class_name=impl["class_name"], + tier=int(impl.get("tier", concept["tier"])), + scope_policy=concept["scope_policy"], + backing_op=concept["backing_op"], + gpu_bound=bool(concept["gpu_bound"]), + ) + + +# --------------------------------------------------------------------------- # +# Runtime # +# --------------------------------------------------------------------------- # +def get_rule_registry() -> Any: + """Return the validator runtime's rule registry, or fail closed. + + Tries the Kit core package first, then the standalone package. Raises + ``ValidationRuntimeUnavailable`` if neither imports — there is no CLI path. + """ + try: + from omni.asset_validator.core import ValidationRulesRegistry # type: ignore + + return ValidationRulesRegistry + except ImportError: + pass + try: + from omni.asset_validator import CategoryRuleRegistry # type: ignore + + return CategoryRuleRegistry() + except ImportError as exc: # pragma: no cover - environment dependent + raise ValidationRuntimeUnavailable(_RUNTIME_UNAVAILABLE) from exc + + +def get_validation_engine_cls() -> Any: + """Return the ``ValidationEngine`` class, or fail closed. + + Kit exposes it at ``omni.asset_validator.core``; the standalone package + exposes it at ``omni.asset_validator`` (no ``.core``). Try both so the same + executor works in either runtime. + """ + try: + from omni.asset_validator.core import ValidationEngine # type: ignore + + return ValidationEngine + except ImportError: + pass + try: + from omni.asset_validator import ValidationEngine # type: ignore + + return ValidationEngine + except ImportError as exc: # pragma: no cover - environment dependent + raise ValidationRuntimeUnavailable(_RUNTIME_UNAVAILABLE) from exc + + +def iter_registered_rules(rule_registry: Any) -> Iterable[type]: + """Yield every registered rule *class* (collision-aware enumeration). + + Identity lives on the class as ``__module__`` and ``__name__``. This adapts + to the differing registry shapes across runtimes but never collapses rules + to bare names. Fail-closed: if no enumeration entry point is found, raises. + """ + # Scene Optimizer registers its rules on import for discovery. + try: + import omni.scene.optimizer.validators # type: ignore # noqa: F401 + except ImportError: # pragma: no cover - environment dependent + pass + + # Known registry shapes, probed by entry point (never collapsed to bare + # names — matching stays identity-based below): + # - ``registered_rules`` Kit core ValidationRulesRegistry (iterable/callable) + # - ``rules_by_name`` older name->rule map + # - ``rules`` OAV 1.18.0 CategoryRuleRegistry (iterable of classes) + # This is a runtime adapter, not a correctness fallback. Extend here only if + # a new runtime exposes another shape, and only with an entry point that + # yields rule classes carrying real ``__module__`` / ``__name__`` identity. + rules = getattr(rule_registry, "registered_rules", None) + if callable(rules): + rules = rules() + if rules is None: + mapping = getattr(rule_registry, "rules_by_name", None) + rules = mapping.values() if isinstance(mapping, dict) else None + if rules is None: + direct = getattr(rule_registry, "rules", None) + if callable(direct): + direct = direct() + if isinstance(direct, dict): + direct = direct.values() + rules = direct + if rules is None: + raise ValidationRuntimeUnavailable( + "Could not enumerate registered rules from the runtime registry; the " + "registry API shape is unrecognized. Record the gap rather than guessing." + ) + + for rule in rules: + rule_cls = getattr(rule, "rule", rule) # unwrap registration wrappers + if isinstance(rule_cls, type): + yield rule_cls + + +def resolve_rule_class(rule_registry: Any, module: str, class_name: str) -> type: + """Resolve ``(module, class_name)`` to exactly one registered rule class. + + This is the collision-safe core. ``IndexedPrimvarChecker`` exists twice by + bare name but is unique by ``(module, __name__)``. Fail-closed on zero or + multiple matches. + """ + matches = [ + rule_cls + for rule_cls in iter_registered_rules(rule_registry) + if rule_cls.__module__ == module and rule_cls.__name__ == class_name + ] + if not matches: + raise ConceptResolutionError( + f"Rule not registered in this runtime: {module}.{class_name}. " + f"Confirm the providing package was imported." + ) + if len(matches) > 1: + raise ConceptResolutionError( + f"Ambiguous rule identity: {module}.{class_name} matched " + f"{len(matches)} registered classes." + ) + return matches[0] + + +# --------------------------------------------------------------------------- # +# Scoped stage open # +# --------------------------------------------------------------------------- # +def open_scoped_stage(stage_path: str, mask_paths: list[str] | None = None) -> Any: + """Open a stage, optionally masked to ``mask_paths`` (+ the default prim). + + ``Usd.Stage.OpenMasked()`` is the only reliable scoping mechanism for the + Asset Validator (it discards caller ``StageLoadRules`` but preserves the + population mask). Rejects an empty masked sample so the caller never reports + a misleading "0 findings". + """ + from pxr import Sdf, Usd, UsdGeom # deferred runtime import + + if not mask_paths: + return Usd.Stage.Open(stage_path) + + root_layer = Sdf.Layer.FindOrOpen(stage_path) + mask = Usd.StagePopulationMask() + default_prim_path = f"/{root_layer.defaultPrim}" if root_layer.defaultPrim else None + for path in [*mask_paths, default_prim_path]: + if path: + mask.Add(path) + + stage = Usd.Stage.OpenMasked(root_layer, mask) + assert stage.GetDefaultPrim().IsValid(), "masked stage excluded default prim" + + mesh_count = sum( + 1 + for prim in Usd.PrimRange.Stage( + stage, Usd.TraverseInstanceProxies(Usd.PrimDefaultPredicate) + ) + if prim.IsA(UsdGeom.Mesh) + ) + if mesh_count == 0: + raise RuntimeError("masked validation sample contains no meshes") + return stage + + +# --------------------------------------------------------------------------- # +# Selected validation # +# --------------------------------------------------------------------------- # +def validate_concepts( + stage_path: str, + concepts: list[str], + *, + registry: dict[str, Any] | None = None, + mask_paths: list[str] | None = None, + provider: str | None = None, +) -> list[Any]: + """Run the named canonical concepts on a (optionally masked) stage. + + Enables exactly the resolved rule classes — never ``init_rules=True`` — so + only the selected concepts execute. Intended to be invoked once per Tier 1 + batch, and from a bounded ``subprocess`` per target for Tier 2 / Tier 3 so + a slow C++ rule can be killed by the parent (see the runner README). + + Returns the list of issues. Resolution failures fail closed; a missing + runtime raises ``ValidationRuntimeUnavailable``. + """ + reg = registry if registry is not None else load_registry() + rule_registry = get_rule_registry() + engine_cls = get_validation_engine_cls() + + engine = engine_cls(init_rules=False) + for canonical_name in concepts: + impl = resolve_implementation(reg, canonical_name, provider=provider) + rule_cls = resolve_rule_class(rule_registry, impl.module, impl.class_name) + engine.enable_rule(rule_cls) + + stage = open_scoped_stage(stage_path, mask_paths) + return list(engine.validate(stage).issues()) + + +# --------------------------------------------------------------------------- # +# Coverage ledger + completion gate # +# --------------------------------------------------------------------------- # +def coverage_complete( + planned_targets: list[dict[str, Any]], + ledger_entries: list[dict[str, Any]], +) -> bool: + """The completion gate. + + Returns True only when every planned ``(target, concept)`` has a ledger + entry with a resolved status. This is what prevents an agent from declaring + victory while a flagged Tier 3 probe was silently skipped: an unresolved + target has no entry, so the gate stays closed and the report's + ``coverage_ledger.complete`` is False. + """ + planned = { + (t["target"], t["concept"]) + for tgt in planned_targets + for t in _expand_target(tgt) + } + covered = { + (e["target"], e["concept"]) + for e in ledger_entries + if e["status"] in RESOLVED_STATUSES + } + return planned.issubset(covered) + + +def _iter_execution_units( + target: dict[str, Any], +) -> Iterable[tuple[dict[str, str], list[str]]]: + """Yield ``(ledger_unit, mask_paths)`` for each concrete path/pair in a target. + + The mask is built **per unit** so every probe is scoped to exactly its own + geometry — and, critically, a ``pairs`` entry contributes *both* prim paths + to the mask. (The earlier code derived the mask from ``paths``/``mask_paths`` + only, so a pairs-only spatial target produced an empty mask and silently ran + the approval-gated full stage while the ledger logged it as a scoped probe.) + + A target with no concrete paths/pairs yields a single whole-stage unit with + an empty mask; ``run_scope_note`` permits that only for ``whole_stage`` + concepts and otherwise fails closed. + """ + concept = target["concept"] + singles = list(target.get("paths", [])) + list(target.get("mask_paths", [])) + pairs = [list(p) for p in target.get("pairs", [])] + produced = False + for path in singles: + produced = True + yield {"target": path, "concept": concept}, [path] + for pair in pairs: + produced = True + yield {"target": "::".join(pair), "concept": concept}, list(pair) + if not produced: + yield {"target": "", "concept": concept}, [] + + +def _expand_target(target: dict[str, Any]) -> Iterable[dict[str, str]]: + """Yield one ``{target, concept}`` per concrete path/pair (ledger identity). + + Shares its expansion with execution via ``_iter_execution_units`` so the + completion gate and the executor can never disagree about what was planned. + """ + for unit, _mask in _iter_execution_units(target): + yield unit + + +def run_scope_note( + stage_path: str, + scope_note: dict[str, Any], + *, + registry: dict[str, Any] | None = None, + concept_runner: Callable[..., list[Any]] | None = None, + phase: str = "baseline", + provider: str | None = None, +) -> dict[str, Any]: + """Execute a scope note tier-by-tier and build a schema-valid report. + + ``concept_runner(stage_path, concept, mask_paths=...) -> issues`` is + injectable so Tier 2/3 work can be wrapped in a killable subprocess (see the + runner README's driver section). The default runs in-process via + ``validate_concepts``; for Tier 2/3 the caller should pass a subprocess + driver so one slow C++ rule cannot hang the batch. + + Each target's disposition is recorded in the coverage ledger: + - issues found -> ``probed_with_findings`` + - clean -> ``probed_clean`` + - subprocess timeout -> ``timeout_recorded`` (retry masked/standalone) + - runtime unavailable -> ``blocked_validation_runtime`` + Resolution failures (unknown/ambiguous concept) are NOT swallowed — they + raise, because they indicate a malformed plan, not a runtime condition. + """ + reg = registry if registry is not None else load_registry() + run = concept_runner if concept_runner is not None else validate_concepts + + validators: list[dict[str, Any]] = [] + ledger: list[dict[str, Any]] = [] + error_count = 0 + + for target in scope_note.get("targets", []): + concept = target["concept"] + impl = resolve_implementation(reg, concept, provider=provider) # fail-closed + base_entry = { + "name": concept, + "kind": "rule", + "canonical_name": concept, + "module": impl.module, + "class_name": impl.class_name, + } + for unit, mask_paths in _iter_execution_units(target): + if impl.scope_policy != "whole_stage" and not mask_paths: + raise ConceptResolutionError( + f"Concept '{concept}' has scope_policy '{impl.scope_policy}' but its " + f"target supplied no paths or pairs to scope to. A scoped concept " + f"must never fall back to an implicit full-stage run; fix the scope " + f"note (provide paths/pairs, or use the approved full-sweep path)." + ) + try: + issues = run(stage_path, [concept], registry=reg, mask_paths=mask_paths) + except subprocess.TimeoutExpired: + validators.append({**base_entry, "status": "TIMEOUT"}) + ledger.append({**unit, "tier": impl.tier, "status": "timeout_recorded"}) + continue + except ValidationRuntimeUnavailable as exc: + validators.append({**base_entry, "status": "BLOCKED", "notes": str(exc)}) + ledger.append({**unit, "tier": impl.tier, "status": "blocked_validation_runtime"}) + continue + count = len(issues) + error_count += count + validators.append({**base_entry, "status": "FAIL" if count else "PASS", "issues": count}) + ledger.append({ + **unit, + "tier": impl.tier, + "status": "probed_with_findings" if count else "probed_clean", + }) + + complete = coverage_complete(scope_note.get("targets", []), ledger) + return { + "schemaVersion": "1.0.0", + "phase": phase, + "stage": {"identifier": stage_path}, + "validators": validators, + "summary": { + "status": "BLOCKED" if not complete else ("FAIL" if error_count else "PASS"), + "errorCount": error_count, + "warningCount": 0, + }, + "coverage_ledger": {"complete": complete, "entries": ledger}, + } + + +# --------------------------------------------------------------------------- # +# Subprocess runner (killable Tier 2 / Tier 3) # +# --------------------------------------------------------------------------- # +def subprocess_concept_runner( + *, + timeout_seconds: int = 120, + python_executable: str | None = None, + registry_path: str | Path | None = None, +) -> Callable[..., list[Any]]: + """Build a ``concept_runner`` that runs each concept in a child process. + + Tier 2 / Tier 3 rules are C++-heavy and can hang; Python ``signal``/threads + cannot interrupt them. Running each concept in a child process means the + parent can kill it on timeout. Pass the returned callable as + ``run_scope_note(..., concept_runner=subprocess_concept_runner())``. + + The child is invoked as ``python `` with a JSON job on stdin and + a JSON result on stdout — an internal worker protocol, not a CLI: there are + no rule-selection flags. On timeout, ``subprocess.TimeoutExpired`` propagates + (``run_scope_note`` records ``timeout_recorded``). A child that reports the + runtime missing raises ``ValidationRuntimeUnavailable``. + """ + import os + import sys + + executable = python_executable or sys.executable + worker = str(Path(__file__).resolve()) + + def _runner(stage_path, concepts, *, registry=None, mask_paths=None): + job = json.dumps( + { + "stage_path": stage_path, + "concept": concepts[0], + "mask_paths": mask_paths or [], + "registry_path": str(registry_path) if registry_path else None, + } + ) + env = dict(os.environ) + env["PYTHONPATH"] = os.pathsep.join( + [str(Path(worker).parent), env.get("PYTHONPATH", "")] + ) + completed = subprocess.run( + [executable, worker], + input=job, + capture_output=True, + text=True, + timeout=timeout_seconds, + env=env, + ) + if completed.returncode != 0: + raise RuntimeError( + f"validation worker failed (rc={completed.returncode}): " + f"{completed.stderr.strip()[:500]}" + ) + result = json.loads(completed.stdout.strip().splitlines()[-1]) + if result.get("status") == "blocked_validation_runtime": + raise ValidationRuntimeUnavailable(result.get("detail", "runtime unavailable")) + return list(result.get("issues", [])) + + return _runner + + +def _worker_main() -> int: + """Child entrypoint: read one JSON job from stdin, print a JSON result. + + Internal protocol used by ``subprocess_concept_runner`` — not a user CLI. + """ + import sys + + job = json.loads(sys.stdin.read()) + registry = load_registry(job.get("registry_path")) + try: + issues = validate_concepts( + job["stage_path"], + [job["concept"]], + registry=registry, + mask_paths=job.get("mask_paths") or None, + ) + except ValidationRuntimeUnavailable as exc: + print(json.dumps({"status": "blocked_validation_runtime", "detail": str(exc)})) + return 0 + print(json.dumps({"status": "ok", "issues": [str(i) for i in issues]})) + return 0 + + +if __name__ == "__main__": + raise SystemExit(_worker_main()) diff --git a/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/scripts/validation-report.schema.json b/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/scripts/validation-report.schema.json index 4dfa32d9..789c2e66 100644 --- a/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/scripts/validation-report.schema.json +++ b/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/scripts/validation-report.schema.json @@ -2,28 +2,93 @@ "$comment": "SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\nSPDX-License-Identifier: Apache-2.0", "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "USD Performance Tuning Validation Report", + "$comment": "Output of the validation reference executor. Strict/closed. The coverage_ledger is required and machine-enforces the completion gate: the run cannot declare done while a flagged target is unresolved.", "type": "object", - "required": ["schemaVersion", "stage", "validators", "summary", "findings"], + "additionalProperties": false, + "required": ["schemaVersion", "stage", "phase", "validators", "summary", "coverage_ledger"], "properties": { "schemaVersion": { "type": "string" }, + "phase": { "enum": ["baseline", "after"] }, "stage": { "type": "object", + "additionalProperties": false, "required": ["identifier"], "properties": { "identifier": { "type": "string" }, "rootLayer": { "type": "string" } } }, - "validators": { "type": "array", "items": { "type": "object" } }, + "validators": { + "type": "array", + "items": { "$ref": "#/$defs/validator_entry" } + }, "summary": { "type": "object", + "additionalProperties": false, + "required": ["status", "errorCount", "warningCount"], "properties": { - "status": { "type": "string" }, - "errorCount": { "type": "integer" }, - "warningCount": { "type": "integer" } + "status": { "enum": ["PASS", "FAIL", "BLOCKED"] }, + "errorCount": { "type": "integer", "minimum": 0 }, + "warningCount": { "type": "integer", "minimum": 0 } } }, - "findings": { "type": "array", "items": { "type": "object" } } + "coverage_ledger": { "$ref": "#/$defs/coverage_ledger" }, + "findings": { "type": "array", "items": { "type": "object" } }, + "artifacts": { "type": "object" }, + "runtime_context": { "type": "object" }, + "generated_at": { "type": "string" } }, - "additionalProperties": true + "$defs": { + "validator_entry": { + "type": "object", + "additionalProperties": false, + "required": ["name", "kind", "status"], + "properties": { + "name": { "type": "string" }, + "kind": { "enum": ["openability", "rule"] }, + "status": { "enum": ["PASS", "FAIL", "SKIPPED", "TIMEOUT", "BLOCKED"] }, + "canonical_name": { "type": "string", "pattern": "^[a-z][a-z0-9_]*$" }, + "module": { "type": "string" }, + "class_name": { "type": "string" }, + "issues": { "type": "integer", "minimum": 0 }, + "notes": { "type": "string" } + }, + "allOf": [ + { + "$comment": "Rule entries must carry resolved identity — proof the collision-safe resolver ran. Never a bare name.", + "if": { "properties": { "kind": { "const": "rule" } } }, + "then": { "required": ["canonical_name", "module", "class_name"] } + } + ] + }, + "coverage_ledger": { + "type": "object", + "additionalProperties": false, + "required": ["complete", "entries"], + "properties": { + "complete": { + "type": "boolean", + "$comment": "true only when no flagged target is unresolved. The completion gate keys on this field." + }, + "entries": { + "type": "array", + "items": { "$ref": "#/$defs/ledger_entry" } + } + } + }, + "ledger_entry": { + "type": "object", + "additionalProperties": false, + "required": ["target", "concept", "tier", "status"], + "properties": { + "target": { "type": "string", "minLength": 1 }, + "concept": { "type": "string", "pattern": "^[a-z][a-z0-9_]*$" }, + "tier": { "enum": [1, 2, 3] }, + "status": { + "enum": ["probed_with_findings", "probed_clean", "user_declined", "timeout_recorded", "blocked_validation_runtime"] + }, + "reason": { "type": "string" } + } + } + } } diff --git a/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/scripts/validation-scope-note.schema.json b/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/scripts/validation-scope-note.schema.json new file mode 100644 index 00000000..d620a048 --- /dev/null +++ b/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/scripts/validation-scope-note.schema.json @@ -0,0 +1,63 @@ +{ + "$comment": "SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\nSPDX-License-Identifier: Apache-2.0", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "USD Validation Scope Note", + "$comment": "Input plan consumed by the validation reference executor. Concepts are canonical names resolved against validator-concepts.json; raw runtime class names are forbidden by pattern.", + "type": "object", + "additionalProperties": false, + "required": ["scope", "concepts", "targets", "tier_assignments", "selection_reason", "artifact_paths"], + "properties": { + "scope": { + "enum": ["minimum_openability", "targeted", "masked_stage_spot_check", "approved_full_sweep", "structural_only"] + }, + "concepts": { + "type": "array", + "minItems": 0, + "items": { + "type": "string", + "pattern": "^[a-z][a-z0-9_]*$", + "$comment": "Canonical concept name; must resolve in validator-concepts.json (CI cross-check)." + } + }, + "targets": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["concept"], + "properties": { + "concept": { "type": "string", "pattern": "^[a-z][a-z0-9_]*$" }, + "paths": { "type": "array", "items": { "type": "string" } }, + "mask_paths": { "type": "array", "items": { "type": "string" } }, + "pairs": { + "type": "array", + "items": { + "type": "array", + "items": { "type": "string" }, + "minItems": 2, + "maxItems": 2 + } + } + } + } + }, + "tier_assignments": { + "type": "object", + "propertyNames": { "pattern": "^[a-z][a-z0-9_]*$" }, + "additionalProperties": { "enum": [1, 2, 3] } + }, + "selection_reason": { "type": "string", "minLength": 1 }, + "artifact_paths": { "type": "array", "items": { "type": "string" } }, + "full_sweep": { + "type": "object", + "additionalProperties": false, + "required": ["status"], + "properties": { + "status": { "enum": ["skipped", "approved"] }, + "reason": { "type": "string" }, + "approved_by_user": { "type": "boolean" } + } + }, + "estimated_time": { "enum": ["fast", "minutes", "long"] } + } +} diff --git a/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/scripts/validator-concepts.schema.json b/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/scripts/validator-concepts.schema.json new file mode 100644 index 00000000..a0d95cb1 --- /dev/null +++ b/skills/omniverse-usd-performance-tuning/references/usd-validation-runner/scripts/validator-concepts.schema.json @@ -0,0 +1,92 @@ +{ + "$comment": "SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\nSPDX-License-Identifier: Apache-2.0", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Canonical Validator-Concept Registry", + "type": "object", + "required": ["schema_version", "concepts"], + "additionalProperties": false, + "properties": { + "schema_version": { "type": "string" }, + "$comment": { "type": "string" }, + "concepts": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/$defs/concept" } + } + }, + "$defs": { + "tier": { "enum": [1, 2, 3] }, + "concept": { + "type": "object", + "additionalProperties": false, + "required": [ + "canonical_name", + "role", + "backing_op", + "tier", + "cost_class", + "gpu_bound", + "scope_policy", + "preferred_provider", + "implementations" + ], + "properties": { + "canonical_name": { + "type": "string", + "pattern": "^[a-z][a-z0-9_]*$", + "$comment": "Lowercase snake_case only; never a runtime class name." + }, + "display_name": { "type": "string" }, + "role": { + "enum": ["safety_gate", "opportunity_detector", "target_scoping", "regression_evidence"] + }, + "backing_op": { + "type": ["string", "null"], + "$comment": "Op key the concept routes to, or null for manual/safety concepts." + }, + "tier": { "$ref": "#/$defs/tier" }, + "cost_class": { "enum": ["cheap", "medium", "expensive", "stage_dependent"] }, + "gpu_bound": { "type": "boolean" }, + "scope_policy": { "enum": ["whole_stage", "per_target_or_sample", "flagged_pairs_only"] }, + "preferred_provider": { "enum": ["so", "oav"] }, + "implementations": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/$defs/implementation" } + }, + "parameter_prerequisite": { "$ref": "#/$defs/parameter_prerequisite" }, + "notes": { "type": "string" } + } + }, + "implementation": { + "type": "object", + "additionalProperties": false, + "required": ["provider", "module", "class_name", "use_for"], + "properties": { + "provider": { "enum": ["so", "oav"] }, + "module": { "type": "string", "minLength": 1 }, + "class_name": { "type": "string", "minLength": 1 }, + "category": { + "type": "string", + "$comment": "Informational registry bucket. Resolution is by (module, class_name); category is not required." + }, + "tier": { "$ref": "#/$defs/tier", "$comment": "Per-implementation tier override (e.g. OAV slow variant)." }, + "use_for": { + "type": "array", + "minItems": 1, + "items": { "enum": ["performance_tuning", "conformance_audit", "safety_gate"] } + } + } + }, + "parameter_prerequisite": { + "type": "object", + "additionalProperties": false, + "required": ["op", "param", "question"], + "properties": { + "op": { "type": "string", "minLength": 1 }, + "param": { "type": "string", "minLength": 1 }, + "question": { "type": "string", "minLength": 1 } + } + } + } +} diff --git a/skills/omniverse-usd-performance-tuning/references/workflow.md b/skills/omniverse-usd-performance-tuning/references/workflow.md index 9e1999d8..0cc0a47f 100644 --- a/skills/omniverse-usd-performance-tuning/references/workflow.md +++ b/skills/omniverse-usd-performance-tuning/references/workflow.md @@ -28,7 +28,9 @@ Each `references/*.md` file starts with a header block: ## The 7-phase canonical flow -Seven required phases (0-6) plus an optional Phase 7 (post-report iteration). +Seven in-flow phases (0-6) plus Phase 7. For broad "optimize this scene" +requests, Phase 7 defaults to 3 scoped iterations unless the user opts out, +asks for a quick pass, or stop criteria apply. For structured milestone lists, preserve this broad-optimization subsequence: `omniverse-usd-performance-tuning` -> `profile-stage:baseline` -> @@ -48,7 +50,7 @@ flowchart TD P4["Phase 4 Per-sub-asset mesh ops"] P5["Phase 5 Stage-level ref replacement and cleanup"] P6["Phase 6 Verify and report"] - P7["Phase 7 Optional iteration"] + P7["Phase 7 Default scoped iteration"] P0 --> P1 --> P2 P2 -->|"already_optimized"| P6 P2 -->|"exit"| P6 @@ -97,7 +99,7 @@ dispatch, the flow may continue in structural-only mode: - **Phase 3 still works** (instancing-readiness is pure USD); flips can be authored. - **Phase 4 SKIPPED** (mesh ops require SO). - **Phase 5 SKIPPED** (no optimized children to remap). -- Phase 6: `profile-stage` AFTER + `usd-validation-runner` re-validation (pre-mutation USD stack only) + `optimization-report` with verdict `structural-only` and a clear note explaining that SO operations did not run. +- Phase 6: `profile-stage` AFTER + `usd-validation-runner` re-validation (pre-mutation USD stack only) + `optimization-report` with `workflow_mode: structural_only` (the `verdict` stays in its enum — `neutral` if no metrics changed) and a `notes` entry explaining that SO operations did not run. This is the path E2E test scenarios commonly hit. @@ -138,11 +140,25 @@ Five steps (2a-2d) feeding the gate at 2e, plus optional 2f if the user chooses SA's asset_boundary_suggestions field already promotes hash-aligned cut points. -2c Phase-aware preliminary validator sweep (USE: usd-validation-runner) - Validation-runner loads `usd-validation-runner/references/validation-scoping.md` for tier - selection. Subset is driven by SA's phase_recommendation (see - Decision Tree in that reference for the full rules). - Output: a "findings corpus" that informs 2e and Phase 4 op selection. +2c Phase-aware validation scope + selected probes (USE: usd-validation-runner) + Read `usd-validation-runner/README.md` before writing or running + validator code. The runner first builds a selected validation plan from + SA's summary_counts, phase_recommendation, validation_scope, and + flagged_assets; then it runs only the selected rules/probes. + Validators are named by canonical concept (validator-concepts.json) and + executed via scripts/usd_validation_executor.py — never by bare class + name or a hand-written script. A flagged Tier 3 target's scoped probe is + mandatory (no approval); only the full-stage version is approval-gated. + Output: a compact scope note/artifact (validation-scope-note.schema.json) + plus a findings corpus that informs 2e and Phase 4 op selection. The + validation-report's coverage_ledger must be complete (every flagged + target resolved) before advancing. + + Large-stage guardrail: if resolved stage size is unknown or >100 MB, + composed prim count is >10,000, mesh/prototype count is high, the target + is customer-scale CAD/BIM/MEP/factory/plant/city, or the ask is + performance optimization rather than formal conformance, do not run a + default full-stage AV/SO sweep. Ask before full sweep. 2d Stage-level instancing assessment (USE: dedupe-candidates output from 2b) For composed stages: are existing references actually instanceable? @@ -215,6 +231,9 @@ Owner: `so-interpret-validators` (build op chain from Phase 2c findings; T3 neve geometry, non-extracted sub-hierarchies). If the assembly root has 0 mesh prims after extraction (pure Xform/reference hierarchy), skip it but log the skip decision. + Consume every `phase4_targets[]` entry; do not filter the manifest + down to prototype paths. An `assembly_root` target with retained + meshes is a mesh-optimization target, not a stage-cleanup-only target. - Composed stage: each referenced asset from Phase 2a - Monolithic-as-is: the monolith itself (N=1) @@ -232,7 +251,10 @@ Owner: `so-interpret-validators` (build op chain from Phase 2c findings; T3 neve 4c Per-target op chain (built from Phase 2c findings via so-interpret-validators): Honor prototype-first ordering: prototypes BEFORE non-prototype targets - so changes propagate. + so changes propagate. Then run the same evidence-selected mesh op chain + on every non-prototype mesh target, including an `assembly_root` target + when it retained local meshes. Stage-level cleanup comes later; it does + not replace mesh operations for geometry left in the assembly. **Internal geometry removal runs FIRST** when SA flagged containment pairs with opaque enclosures: findOccludedMeshes (analysis) → removePrims (user-confirmed deletion) @@ -243,25 +265,56 @@ Owner: `so-interpret-validators` (build op chain from Phase 2c findings; T3 neve policy before mutation. Prefer meshCleanup for vertex welding; reach for standalone mergeVertices only when the user explicitly needs that - upstream-documented behavior (see pipelines.md - "mergeVertices op vs meshCleanup.mergeVertices parameter"). - Honor ordering invariants from - so-run-operations/references/pipelines.md (merge caveats: never if - instanced/streaming). + upstream-documented behavior — the op mechanics and the + meshCleanup.mergeVertices parameter live upstream, resolved via + `references/upstreams/usd-optimize.md`. + Honor the ordering invariants in the "Operation ordering invariants" + section below (merge caveats: never if instanced/streaming). Save each optimized output to a NEW path (don't overwrite source). 4d Per-target cheap re-verify Re-run cheap validators on each optimized output to catch obvious regressions before stage assembly. Defers full re-validation to Phase 6. - After restructure/decompose, follow the "Post-Restructure Validation - Strategy" in validation-scoping.md — do not re-compose and sweep. + After restructure/decompose, follow the "Post-Restructure / + Post-Decompose Validation Strategy" in usd-validation-runner/README.md + — do not re-compose and sweep. + +4e Target completion gate (machine-checked; mirrors the validation + coverage_ledger): + Record each Phase-4 target in the optimization-report's top-level + `target_coverage.entries[]` with `path`, `role`, the default-predicate + `mesh_count`, and a `disposition` + (optimized | skipped_zero_meshes | skipped_user_declined | blocked). + Use the restructure roles (assembly_root | prototype | shared_layer | + loadable_subasset) after a restructure, and `monolith` for a + non-restructured optimize-as-is target (N=1). + `target_coverage.complete` is true only when every entry is resolved + (the first three dispositions); a `blocked` or absent target keeps it + false and the report is not final. A diagnosis-only / optimize-as-is run + with no Phase-4 work is valid with `entries: []` and `complete: true`. + The report author cannot self-attest coverage of a target that was never + enumerated, so the gate reconciles against the manifest(s). Reconciliation + is NOT optional once a restructure happened: whenever any entry has a + restructure role, record the source manifest(s) in + `target_coverage.source_manifests[]` (one per iteration). The gate + auto-loads them — and also accepts `--manifest` — and fails closed if a + restructure report has none: + python3 optimization-report/scripts/validate_report.py \ + [--manifest ] \ + [--manifest ] + The final report MUST cover the UNION of every iteration's + `phase4_targets[]` (a target listed in iter-1 but dropped from iter-2's + manifest is still owed coverage), `skipped_zero_meshes` is accepted only + when the manifest's authoritative `mesh_count` is 0, and any uncovered or + unresolved target exits non-zero. This is the gate that catches a + retained-mesh `assembly_root` left un-optimized. A `monolith`-only run + needs no manifest. Runtime branch: Kit: ops run via selected SO Python API inside Kit standalone: ops run via selected SO Python API or standalone wrapper - All Python scripts probe the selected runtime first; do not assume - `SceneOptimizerCore.getInstance()` when the runtime exposes only - `acquire_interface().execute_operation(...)`. + All Python scripts follow so-run-operations/references/invocation.md; do not + pass plain pxr.Usd.Stage objects directly to Scene Optimizer operation APIs. ``` ### Phase 4.5 - Layer cleanup after destructive in-place ops @@ -311,7 +364,7 @@ Owner: `apply-restructure` (mode=ref_remap). Same skill as Phase 2f - both phase If regressed > 5%: warn If regressed > 20%: critical, recommend revert/halt -6e optimization-report (final step of in-flow phases; honors the skill's +6d optimization-report (final step of in-flow phases; honors the skill's existing "final step" contract). Populate against the optimization-report schema (`scripts/optimization-report.schema.json` within that reference). Match optimization-report/references/optimization-report-template.md. Include baseline metrics @@ -323,7 +376,7 @@ Owner: `apply-restructure` (mode=ref_remap). Same skill as Phase 2f - both phase When returning structured plans or runtime-test milestone lists, label Phase 6a exactly `profile-stage:after`. -### Phase 7 - Iterate (optional, post-report, agent-orchestration only) +### Phase 7 - Iterate (default 3 scoped passes, post-report, agent-orchestration only) ``` 7a Compute "untapped options" - the diff between what was done and what could @@ -335,25 +388,38 @@ exactly `profile-stage:after`. - Phase 4 adaptive batching paused remaining sub-assets due to resource budget; remainder script generated -7b Offer to user (freeform, agent discretion): - "Optimization complete. Verdict: . The following were not - applied this pass: [list]. Want to run another iteration with any - of these enabled, or with adjusted parameters?" +7b Default to 3 optimization iterations for broad "optimize this scene" + requests unless the user opts out, asks for a quick pass, or the request is + diagnosis-only. Each iteration writes an interim report/update before the + next begins. -7c If user accepts iteration: - Loop back to the relevant phase (typically Phase 2c with adjusted - validator subset, or Phase 4 with new ops in the chain). Keep the - baseline metrics from the FIRST pass (don't re-baseline). +7c Iteration 1 follows the normal Phase 0-6 flow. Iterations 2 and 3 are + lighter scoped passes: reuse prior SA/profile/validation evidence, start + from the previous report's untapped options, and run only targeted/delta + probes needed to choose the next operation set. -7d If user declines: - Flow truly ends. The Phase 6e report stands as the final deliverable. +7d Loop back to the relevant phase (typically Phase 2c with adjusted selected + probes, or Phase 4 with new ops in the chain). Keep baseline metrics from + the FIRST pass (don't re-baseline). + +7e Stop before iteration 2 or 3 if no useful untapped options remain, the + previous pass regressed materially, the user opted out, or the next pass + would only repeat work. ``` -Phase 7 is optional. Always compute the "untapped options" list for transparency in the report (so the user can see what was held back even if they don't iterate). Use discretion on whether to actively offer iteration: do not nag if the user asked for "quick pass." +Phase 7 is a default three-pass posture for broad optimization, not permission +to run three full workflow reruns. Later passes are expected to be cheaper +because they reuse evidence and narrow scope. Revalidation in iterations is +same-or-narrower by default; expanded validation scope, Tier 3 cross-component +probes, full sweeps, or newly destructive operations require explicit user +approval. Always compute the "untapped options" list for transparency in the +report, even if the user opts out. ## Validator-stack matrix -The `usd-validation-runner` skill is the master router. For tier 1/2/3 detail, JSON plan template, and scene-aware adjustment rules, read `usd-validation-runner/references/validation-scoping.md`. +The `usd-validation-runner` reference is the master router. It owns tier 1/2/3 +detail, selected-probe planning, full-sweep approval, JSON plan shape, and +scene-aware adjustment rules. ### Pre-Mutation USD Stack @@ -365,15 +431,15 @@ only through their owning workflow when the user explicitly asks for them. ### Performance stack (scoped) -(read `usd-validation-runner/references/validation-scoping.md`) -> `so-run-validators` -> `so-interpret-validators`. +`usd-validation-runner` selected plan -> `so-run-validators` -> `so-interpret-validators`. ### Phase-aware subset -Owned by `usd-validation-runner/references/validation-scoping.md` → *Decision Tree*. Summary: +Owned by `usd-validation-runner/README.md`. Summary: - `structuring` → minimum-openability + targeted AV blockers only. -- `optimization` → minimum-openability + scoped AV + perf stack (Tier 1+2; Tier 3 only on flagged targets). -- `already_optimized` → minimum-openability + scoped AV + perf Tier 1 only. +- `optimization` → minimum-openability + scoped AV + perf stack (Tier 1 cheap whole-stage stats/probes + Tier 2 on flagged targets; Tier 3 scoped probes mandatory on flagged targets, full-stage Tier 3 requires approval). +- `already_optimized` → minimum-openability + scoped AV + Tier 1 cheap whole-stage stats/probes only. ## Operation ordering invariants @@ -401,6 +467,9 @@ checkout through `references/upstreams/usd-optimize.md`. - Common chain: `fitPrimitives` -> `deduplicateGeometry` -> `organizePrototypes`. - Prototype targets run before non-prototype targets; parallelize within each dependency group when resource budget allows. +- "Stage-level operations last" means an additional assembled-root cleanup + pass after per-target mesh work. It does not mean skip mesh operations for + local meshes left behind in an `assembly_root` Phase 4 target. - Bounded-loss or destructive operations run only after `operation-safety.md` confirmation. - Per-operation argument defaults and caveats come from @@ -420,16 +489,27 @@ request or as part of bespoke triage: fires and you need a breakdown before deciding between `removeSmallGeometry`, `decimateMeshes`, and `merge`. - `sparseMeshes` — exposes meshes with very low per-face vertex density; - often a sign of poor authoring or failed import. Outside the default - flow but useful when investigating draw-call counts. + often a sign of poor authoring or failed import. Treat as a Tier 2 targeted + medium probe through `usd-validation-runner`, not a cheap whole-stage default. - `utilityFunction` — meta-utility op for ad-hoc SO scripting; rarely the right tool but available when one of the recipe skills needs it. See `references/operations/utilityFunction.md`. -For lossless coincidence/occlusion analyzers (`findCoincidingGeometry`, -`findFlatHierarchies`, `findOverlappingMeshes`), -prefer running them through `so-interpret-validators` once that skill -wires them in (currently future-candidate per `_curation.json`). +The lossless coincidence/occlusion analyzers (`findCoincidingGeometry`, +`findFlatHierarchies`, `findOverlappingMeshes`) are wired as live analysis ops: +prefer running them through `so-interpret-validators`, which routes them from +validator findings. + +If you do run an analysis-only op on user request, summarize its findings as +optimization candidates, not as raw dumps: + +- `countVertices` → high-poly triage: flag the heaviest meshes as + `decimateMeshes` / `removeSmallGeometry` candidates. +- `findFlatHierarchies` → restructuring candidates: route to `flattenHierarchy` + (Xform collapse) or hierarchy dedupe. +- `findCoincidingGeometry` / `findOverlappingMeshes` → duplicate/overlap + candidates: route to `deduplicateGeometry`, `removeSmallGeometry`, or flag for + manual review. They produce a report, not a change. `findOccludedMeshes` is now wired into the Phase 4 op chain via `config-from-evidence.md` — it runs first (before all other ops) on @@ -442,18 +522,18 @@ SA-flagged containment pairs with opaque enclosures, followed by |---|---| | Phase 0: direct SO execution requested but SO unavailable | Halt with `blocked_missing_scene_optimizer`; do not substitute another workflow. | | Phase 0: requested SO op absent from the loaded runtime | Halt with `blocked_missing_so_operation`; surface supported alternatives if any. | -| Phase 0: broad optimization request, SO unavailable, and user declines install | Switch to structural-only path. Skip Phases 4-5; report `structural-only` in 6e. | +| Phase 0: broad optimization request, SO unavailable, and user declines install | Switch to structural-only path. Skip Phases 4-5; set `workflow_mode: structural_only` in the 6d report (verdict stays in its enum). | | Phase 0: User chooses "exit" at install prompt | Exit with reason "user declined runtime setup". | | Phase 1a: profile-stage fails to open the asset | Halt with diagnostic; the asset cannot be optimized if it cannot be opened. | -| Phase 2c: SA's `phase_recommendation = already_optimized` | Skip Phases 2d-5; jump to Phase 6 verify; produce report with verdict `no-op-needed`. | -| Phase 2e: User chooses "exit" at restructure gate | Skip to Phase 6e and write a diagnosis-only report. | +| Phase 2c: SA's `phase_recommendation = already_optimized` | Skip Phases 2d-5; jump to Phase 6 verify; produce report with `workflow_mode: no_op` and `verdict: neutral`. | +| Phase 2e: User chooses "exit" at restructure gate | Skip to Phase 6d and write a diagnosis-only report. | | Phase 2e: User chooses "optimize as-is" | Skip Phase 2f; continue to Phase 3 with the original stage. | | Phase 3b: All instancing candidates fail readiness | Skip Phase 3 result-application; continue to Phase 4. Note in report. | -| Phase 4d: A target's optimized output fails cheap re-verify | Discard that target's output; continue with other targets. Report failure in 6e. | +| Phase 4d: A target's optimized output fails cheap re-verify | Discard that target's output; continue with other targets. Report failure in 6d. | | Phase 6c: Verdict = regressed > 20% (critical) | Recommend revert (do not publish); user decides whether to publish anyway. | | Phase 6c: Verdict = `mixed` | Report honestly; do not present as success. | -| Phase 6e: optimization-report writes successfully | In-flow phases end. Optional Phase 7 may follow. | -| Phase 7: User declines iteration | Flow truly ends. The Phase 6e report stands as the final deliverable. | +| Phase 6d: optimization-report writes successfully | In-flow pass ends. Phase 7 may continue into the next scoped iteration unless the user opted out or stop criteria apply. | +| Phase 7: User declines iteration | Flow truly ends. The Phase 6d report stands as the final deliverable. | ## Expected duration hints (typical large stages: ~100K prims, ~200K meshes) @@ -464,9 +544,10 @@ These are guidance for setting user expectations and timeout windows, not hard S | Phase 0 | < 1 min once user choices are recorded | | Phase 1 | ~5 min (profile open + SA pass) | | Phase 2c structural validators | ~2 min | -| Phase 2c Tier 1 perf validators | ~5 min | +| Phase 2c Tier 1 cheap whole-stage stats/probes | ~5 min | | Phase 2c Tier 2 perf validators | ~30 min | -| Phase 2c Tier 3 perf validators | hours - always confirm before running | +| Phase 2c Tier 3 perf validators (scoped to flagged targets/pairs) | minutes - mandatory when flagged | +| Phase 2c Tier 3 perf validators (full-stage) | hours - always confirm before running | | Phase 4 per target | ~10-30 min depending on op chain | | Phase 5 ref-remap | ~few min for typical impact sets | | Phase 6 re-validation | same as Phase 2c | diff --git a/skills/omniverse-usd-performance-tuning/skill-card.md b/skills/omniverse-usd-performance-tuning/skill-card.md index 48d29ebc..4671d606 100644 --- a/skills/omniverse-usd-performance-tuning/skill-card.md +++ b/skills/omniverse-usd-performance-tuning/skill-card.md @@ -1,14 +1,15 @@ ## Description:
-Top-level workflow skill for USD performance diagnosis and optimization. Use for slow loading, high memory, low FPS, or 'optimize my scene' requests; delegates auth/runtime setup to Phase 0 owners.
+Top-level workflow skill for USD performance diagnosis and optimization, used for slow loading, high memory, low FPS, or generic scene optimization requests.
This skill is ready for commercial/non-commercial use.
-## Owner: NVIDIA
+## Owner +NVIDIA
### License/Terms of Use:
-Apache 2.0
+Apache-2.0
## Use Case:
-Developers and engineers use this skill to diagnose and optimize USD scene performance — addressing slow loading, high memory usage, low FPS, and GPU crashes — within NVIDIA Omniverse workflows.
+Developers and engineers working with USD scenes who need to diagnose and resolve performance issues such as slow loading, high memory usage, low FPS, or GPU crashes in NVIDIA Omniverse workflows.
### Deployment Geography for Use:
Global
@@ -21,20 +22,33 @@ Mitigation: Review and scan skill before deployment.
- [Workflow Reference](references/workflow.md)
- [Skill Map](references/skill-map.md)
- [USD Structure Assessment](references/usd-structure-assessment/README.md)
-- [Scene Optimizer Operations](references/so-run-operations/README.md)
+- [Scene Optimizer Operations](references/operations/README.md)
+- [Setup USD Performance Tuning](references/setup-usd-performance-tuning/README.md)
- [USD Validation Runner](references/usd-validation-runner/README.md)
-- [Profile Stage](references/profile-stage/README.md)
-- [Compare Profiles](references/compare-profiles/README.md)
- [Optimization Report](references/optimization-report/README.md)
-- [Setup USD Performance Tuning](references/setup-usd-performance-tuning/README.md)
-- [CAD Conversion Advisor](references/cad-conversion/README.md)
+- [Scene Optimizer Run Operations](references/so-run-operations/README.md)
+- [Compare Profiles](references/compare-profiles/README.md)
+- [Profile Stage](references/profile-stage/README.md)
## Skill Output:
-**Output Type(s):** [Analysis, Shell commands, Files]
-**Output Format:** [Markdown and JSON structured reports]
+**Output Type(s):** [Analysis, Shell commands, Configuration instructions, Files]
+**Output Format:** [Markdown with structured JSON reports]
**Output Parameters:** [1D]
-**Other Properties Related to Output:** [Produces optimization-report JSON conforming to optimization-report.schema.json and rendered HTML/Markdown summaries]
+**Other Properties Related to Output:** [Produces optimization-report.schema.json-conforming reports and HTML previews via render_preview.py]
+ +## Evaluation Tasks:
+NVSkills-Eval 3-Tier evaluation (external profile): Tier 1 static validation (9 checks, 10 findings), Tier 2 deduplication analysis (2 checks, 17 findings). Tier 3 live agent evaluation not available in this report.
+ +## Evaluation Metrics Used:
+Reported benchmark dimensions:
+- Security: Checks whether skill-assisted execution avoids unsafe behavior such as secret leakage, destructive commands, or unauthorized access.
+- Correctness: Checks whether the agent follows the expected workflow and produces the correct final output.
+- Discoverability: Checks whether the agent loads the skill when relevant and avoids using it when irrelevant.
+- Effectiveness: Checks whether the agent performs measurably better with the skill than without it.
+- Efficiency: Checks whether the agent uses fewer tokens and avoids redundant work.
+ + ## Skill Version(s):
0.1.0 (source: frontmatter, pyproject.toml)
diff --git a/skills/omniverse-usd-performance-tuning/skill.oms.sig b/skills/omniverse-usd-performance-tuning/skill.oms.sig index 5aaecdbc..bd9e75ae 100644 --- a/skills/omniverse-usd-performance-tuning/skill.oms.sig +++ b/skills/omniverse-usd-performance-tuning/skill.oms.sig @@ -1 +1 @@ -{"mediaType":"application/vnd.dev.sigstore.bundle.v0.3+json","verificationMaterial":{"x509CertificateChain":{"certificates":[{"rawBytes":"MIICgzCCAgmgAwIBAgIUKIyS7SxNteQIiWzK1dWj85E6520wCgYIKoZIzj0EAwMwVTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjEpMCcGA1UEAwwgTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBJQ0EgMDEwHhcNMjYwNDAxMDAwMDAwWhcNMjgwNDIyMTUzMzA5WjBUMQswCQYDVQQGEwJVUzEbMBkGA1UECgwSTlZJRElBIENvcnBvcmF0aW9uMSgwJgYDVQQDDB9OVklESUEgQWdlbnQgU2tpbGxzIFNpZ25pbmcgMDAxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEYoRM9bQl/dGlwSRNi6bTpIJUXH8Nv9GciP6LSflJYYMLCc296kpyuTSsk5ddbAWiDcFX3C/ydX3jwc+qCLYP6uHy9XphyLjOQ27Yb2J6rBLVtRBS1mgGco/Gr7fL6ODco4GaMIGXMB0GA1UdDgQWBBRQ/5ZW3nJ6lmo9SVk7I15o7UGmpTAfBgNVHSMEGDAWgBRPGpILxMBBleJSsBGjrMKsby1CgjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9vY3NwLm5kaXMubnZpZGlhLmNvbTAKBggqhkjOPQQDAwNoADBlAjAUygu/GiOCIXrgGr4SmLgeEVDcEitfFUv7ALbvLVGVyMysB3mxmO/uInZfXzWcJZsCMQDxuoxj4ZmO30jhkPIcCxGFCOvnUsnfU3TfGcouYm4M6iRpbKvtVnHPiy4bi6pcKf0="},{"rawBytes":"MIICiDCCAg6gAwIBAgIUZsIuSv9NkpJCNqtYEfCouVv5BzowCgYIKoZIzj0EAwMwUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTAgFw0yNjA0MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowVTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjEpMCcGA1UEAwwgTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBJQ0EgMDEwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASI72cR3ctKGg4VWnB3bNja6g1Z2PnOmFEopkPof+QeIcPk9rT+g9MjJnq51EQXL93a7C2GJ9J985G4o2V85VD7wJ1RaXhluHW2rf3y8bQGeAYaKMr5s/hUgn+M3/9WlWejgaAwgZ0wHQYDVR0OBBYEFE8akgvEwEGV4lKwEaOswqxvLUKCMB8GA1UdIwQYMBaAFItnoAjjfuCEUvzyvWyI2vOGvwPjMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL29jc3AubmRpcy5udmlkaWEuY29tMAoGCCqGSM49BAMDA2gAMGUCMQCeIMMfAbyzPDacw2MxG+Yt1cikrJX/DVxiGfXuHmkkXn6VgSzE79+lkqDErpVO2gYCMCNEColOyvUvkzZGUEI1hQ3PfMgi3FIo9tHoBKMw4/wGBLFpu/0ubtmbBXM6/UMOEw=="},{"rawBytes":"MIICRTCCAcygAwIBAgIUeJdY3rV86EdvFmG7L8LJBsyQFYkwCgYIKoZIzj0EAwMwUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTAgFw0yNjA0MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IABAYpiXCDjJ9NT2eSDhyHJVSw1Tbze18cGG2F/578oWvHxg23eQAhNRYdq88i1iOshZSO6C29doKui5Xpmo/7Ctw9Sx4PP2RzOmIuOLCuTdNtKcTRwi4GEsd5BAFvWj42M6NjMGEwHQYDVR0OBBYEFItnoAjjfuCEUvzyvWyI2vOGvwPjMB8GA1UdIwQYMBaAFItnoAjjfuCEUvzyvWyI2vOGvwPjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2cAMGQCMCwtAjWLaNwgGWNCgdyNoTyvNhqWRECRJV2r3+7w8g0PL6NHLOsbkgE09BH95h8XlgIwTaQmbbUh2ChAJ5TA1wRiVDnCcvbzHlZl2jM2FcwQQZlk19LOAbyGMRixbu2Ww/rj"}]},"tlogEntries":[]},"dsseEnvelope":{"payload":"ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YxIiwKICAic3ViamVjdCI6IFsKICAgIHsKICAgICAgIm5hbWUiOiAib21uaXZlcnNlLXVzZC1wZXJmb3JtYW5jZS10dW5pbmciLAogICAgICAiZGlnZXN0IjogewogICAgICAgICJzaGEyNTYiOiAiNzYwNjFjZGZmYjBmMzVmMWYyOTE2MWM1YWQ0NmRmNGI3OTllYjI0ZWQ1MzliNjQ1OTkyMjlhNDRhMGE0NjZiYSIKICAgICAgfQogICAgfQogIF0sCiAgInByZWRpY2F0ZVR5cGUiOiAiaHR0cHM6Ly9tb2RlbF9zaWduaW5nL3NpZ25hdHVyZS92MS4wIiwKICAicHJlZGljYXRlIjogewogICAgInNlcmlhbGl6YXRpb24iOiB7CiAgICAgICJoYXNoX3R5cGUiOiAic2hhMjU2IiwKICAgICAgImFsbG93X3N5bWxpbmtzIjogZmFsc2UsCiAgICAgICJtZXRob2QiOiAiZmlsZXMiLAogICAgICAiaWdub3JlX3BhdGhzIjogWwogICAgICAgICIuZ2l0aHViIiwKICAgICAgICAiLmdpdGF0dHJpYnV0ZXMiLAogICAgICAgICIuZ2l0aWdub3JlIiwKICAgICAgICAiLmdpdCIKICAgICAgXQogICAgfSwKICAgICJyZXNvdXJjZXMiOiBbCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJTS0lMTC5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiYTFkOWM1ODJhZWQxMmMwMDcxMWI2YjlkMzE1MWJhMjQzOTdjYjczZDg5OWY0NzIzYWEyNDg2MDk4ZTFjNWU1YSIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogImFnZW50cy9vcGVuYWkueWFtbCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMDMxMjEzY2JiOTVmYzBkMWExZWUzNzczOTYyNzEzNjdmMjM0YzgzYWZmNjNhNWVkNWUwNTYyZjE3M2ZhODU3YiIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogImV2YWxzL2V2YWxzLmpzb24iLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImY1OWE5ZWNlZDVmMzVmNzE4ZmZkNDE4OTE2NTc3NzFmZjg2MmY0NGI3YTFiOTYzOWY2NmMxN2QwY2RhZDkzYmQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL2NhZC1jb252ZXJzaW9uL1JFQURNRS5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMzU1YTJiZTcxMjBlMTQ3Mjk1YzdiYTJlYjVkOWUzYzI3M2JhZmI0MjM3OTc2YTY3MjFjNmMzYmQyMjlhMTRiNCIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvY2FkLWNvbnZlcnNpb24vc2NyaXB0cy9jb252ZXJzaW9uLXJlcG9ydC5zY2hlbWEuanNvbiIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiODViZmY2MTYyZTY5N2U5NzkyNTFmMWJiNWJlMmY4N2I0NzE1NzkwZTllNDRlMjZhMmQ5MjllYmJkMmFiM2ZhZSIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvY29tcGFyZS1wcm9maWxlcy9SRUFETUUubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjEyZTk0MDg4OGQzYjY5YWFhMDdlZjJiZWViMTM5YzNkMzUxZDIyMzkxYjdkNzM3MWM1MTM3OWQ5OWUyYzRmMjkiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL2NvbXBhcmUtcHJvZmlsZXMubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjYwYzU3NDE5ZjQzYWVlYTM3ZWYyYzNhMWYwY2Q5ZjUzZTdlZDFiNDU0NmJkM2M0NDM0NWI4OWE0MzEzZDdkYjciCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29tbml2ZXJzZS1hdXRoZW50aWNhdGlvbi9SRUFETUUubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImVlNGRjYjMwNTM0ZGM5NGU3MDljYzI1MTI5Njc0MDkzM2ZiZDE2NzM3NDkwODNhMWQ4NGE4YmJlZWNlN2YyNzEiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvQ0xBU1NJRklDQVRJT04ubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjJmNjI1YTExMTMxODAxYjFjMDMzNjVhOTNiYmNmN2Q0YTk1Zjk3YjM2NTRhNTQ1ZjM3ZmMwMjUwYjdmZDRiZTAiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvRVhFQ1VUSU9OLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI0MDAzOTRiZWEyOWFjYzRlN2M3MjhjMDZjZTk4NGM4MjU5NTkzOTExMzA3ZmM4NmQyN2E2Y2M1ZTkwMDg4MjVhIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL1JFQURNRS5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiYTE5ZDU2ZTViNGI5ZDUxZWU2NzFlMGVkNWJkMDQ3YWM3NDFmYTdlYWVkOTg3MWMwYzA0YTlhYWFmZDVjZDJkOCIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9fY3VyYXRpb24uanNvbiIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiYjJhZTVhNzZkOTVmZWYxNWNmNjAzM2EwZDJhNDE2Y2U3ZmViYmZmMmYyYTU3ZDM0OWNlNTQ3OGNkNGNjZGIxZiIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9fdGVtcGxhdGUubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjIwOGQxMjZkYzU3OWFiZDNiNzQwYWY1YTY0ODc5NDUyOTdjOGU4NDMxZjYwMDA5OTQ3NTkwYjk2NTUyNGI1Y2EiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvYm94Q2xpcC5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiODA4MmE1OGI5MzIzN2FiOGMzM2Y5OWI1MGI4ZGY2MzY5Nzk0NDRhYWZiZWFlNmQ4Y2M3N2Q3MzkzZDZiOTYzNCIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9jb21wdXRlRXh0ZW50cy5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNTMxMzVmNGVlNzNmZTg1YzllMDFjZGY4OWQ0ZWY1YWQzODhjZWFkOWJlM2VhNmI4N2EzMTdkODdjZWFmYmQ4MyIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9jb3VudFZlcnRpY2VzLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI4NjQyMjFkM2VlNWMyMjVhNzk0MWRmZWQ2OTI1NDA3MDU3YTcwYmQwYzhhYWM2NTZkOGM1NDkxZWY4NTU2YzY2IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL2RlY2ltYXRlTWVzaGVzLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJiMTNhNmRhYWE3YjQyYWYwYTkyYjgzZTYzZTZlMzkwOGY4NjBkMjA2NWE4MmQwYWUxYjIzODQ5ODM4MDhiM2RkIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL2RlZHVwbGljYXRlR2VvbWV0cnkubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjRiZDVjNzVhOTdiOTFhYTE2ZTJlZGQ4YTA5NDUyOGRhYjVhZTk5YjUzOTBkZTJiYjI1MjI2ZjJmZDk1MzFlMWMiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvZGVkdXBsaWNhdGVIaWVyYXJjaGllcy5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNzMzN2NmN2EwOGZjYjkzZDdiZDlkNTYyYjQ4MzE2MDAwYjI1ODQwZTFiNzZiYzRjYzJkM2I2OTkyYjlhMWYxNSIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9kZWxldGVIaWRkZW5Qcmltcy5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNTNhZThiNzJmNmIxMGI3ZmE5ZDhiOTA3MGYwODgwNWZkYTY5MTk5YjI1MjRkMjRmNjczZWI2NjZmZTQxMTNhNyIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9kZWxldGVQcmltcy5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZTU1Y2NjNmE3ODgwMWNlNjdlOTk4MTBlN2FjMWI3YTdiODFjM2VlOGY1MTZhYzc2MGE5NTJiYTFkYjU4MmZiNyIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9kaWNlTWVzaGVzLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJjM2IzNThmMGIxZjA5ZjQ5MmUxYWNjMjQ1Y2EyMGM0NGQ0ZjAwM2NhYTczMjNmMTA3NWNmMjkxZDE0NWM0M2Q4IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL2VkaXRTdGFnZU1ldHJpY3MubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImE4M2Q4ZTAxNmYxMDRkNDExNjRjMDBiNmUxZDEyYmZjYmRjMTJhMDA5YzgzZTk3MTYxNTJiMDM5ZGMzOWQzOWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvZmluZENvaW5jaWRpbmdHZW9tZXRyeS5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiYjY1MWQwYzViODdkNDY1ZTVmNGVkMGI3MDllNmQ5YWJkYTkyZjcyM2M3Mjg1ZDkyZDJlYzU3MmM5MjRmMGU0OSIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9maW5kRmxhdEhpZXJhcmNoaWVzLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIzZGE0MzkyMWQ0YzliNjEyNWM4YmIxYjBhMDQ1ZTQ1YzljNWMyZmU1NDg1NmQ1MGVhYTkwNTE3ZTVlZGFlZmRmIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL2ZpbmRPY2NsdWRlZE1lc2hlcy5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiYzVmMjE1MTM4OTgzYTkzMDRjNWRiMzIyNGYzMTc3N2I5MmE4NGQ0NzY2NWNhZWFjYjk0MTIyMjg3MjMyMmFhYSIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9maW5kT3ZlcmxhcHBpbmdNZXNoZXMubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImFiNDMyNDk3MmU4YTRhNGUxM2IxMTRlOTBhMjFmMTkzNDMzNzQyMjcyNmE2ZGQwZWNhNDNlNjEwMjg2MjQ2NTkiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvZml0UHJpbWl0aXZlcy5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMWY4YjZkNmEwZTY4MGRhMDMzZDY5MWQxNjNmYzQwMzg0ZDNhMmI5Mjg2ZjU5NjM4YmM2NGU3MzU4NDZlODExYSIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9mbGF0dGVuSGllcmFyY2h5Lm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI3MzY0YWM2MDY2MWU4MDQ3NzI4ZmViN2Y2ZDE3NDhjYjlkNjQzNTI1MjU4MTE0ZWJlNDczYTdmM2M2ODZjOWU0IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL2dlbmVyYXRlQXRsYXNVVnMubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImU3YzQ4ZTBkZGQ4NDczNzc5ZjEwNTNiYzQ0OGEyYmJhZDNkZmFhNWU1YzlkMTczZTYzNjdjMzZmZWQyOTUzZGEiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvZ2VuZXJhdGVOb3JtYWxzLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJlYzljMWUwNDU5MjMxOWNlMzBmNzkzYzFmNjQ5NTFkYzQyY2QxM2E5YzZmNDYzZDNkY2QzNmY1Y2FhYTY2YzEyIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL2dlbmVyYXRlUHJvamVjdGlvblVWcy5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZDQ4OGUwNWRkMjgzYTlkNDBiOTI0OTcyNGY0ODEzYWRiNzk0NTg2MmNmMDk5NWQ3ZDk1MTI4YjU1OWQ3NDQ0ZiIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9nZW5lcmF0ZVNjZW5lLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJiOGY4MWJkODNjYTNhYmVkYTNkMDBmYmFkYzc1YzNjYTJlYTRjZDRhMGU0YTAxMjhiMDVhMmYyOTdjNjQ1OGJlIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL21hbmlmZXN0Lmpzb24iLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjRkOTQ2NGRkZDM1ZjMzYTllNjcxM2Q4MWIxMWRjYzRhOWIzODFmMmQ3OTVhNGZmZWUxNGY5NmRhMTc5ZDM3OTAiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvbWFuaWZvbGRNZXNoZXMubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjQwOTMyZTllNDg5NDJlZWExODZjM2ZmOTZiZmE2OGMxZDIyNTkxYjBkYjVhNDY3ZWJkMDdlYzRmZjQwMjg1OGYiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvbWVyZ2UubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImZmOWRiNTM5OTNhYzFjMzhkMmZlMTc1ZTRjMGIzNzFjZmRjMzkzMzZlYjc0NGMxYjNhMDBjMmQ0OGRmYzI0NWUiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvbWVyZ2VWZXJ0aWNlcy5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiYTdhY2EzM2NjODY2ZTE1NzMxYWQ2MjYwZDRjNmNmMDQwZTEyMDJkNDBlODc2ZDcxMTE5NzFmMzg2M2U3MzllNCIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9tZXNoQ2xlYW51cC5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiN2RiNWJjMGYxYmM5OTI0MWUxOTI0NTRlYjJjODM5YjViZGU5OTRjNGQ2YmY1YTJkMzgzMDBiMGVlMzNiMDkwNiIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9vcHRpbWl6ZU1hdGVyaWFscy5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNzYzOTEyMDU4ODJkNjUwOWUwYWU0ODk2M2YyZmYyMGQ2ZmYwMTllMzcxNmI2MzIwNWE0ZmRlYzE1YTE4NjFhNCIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9vcHRpbWl6ZVByaW12YXJzLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI1MmZlZmJkZmJkMzBlMzFiMjUwOTAxYjVjOTMxNjE5M2NjZjU0Zjg3NjA1MmMwMzEzOWM4ZDJjMDQwZTFhYjRiIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL29wdGltaXplU2tlbFJvb3RzLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIzZDBkNGQ1MTc0YjYwNjk3OTUyMTk0M2E2OTVlN2ZhODllZDU0YWIyNjM3MTNjMTY2ZmQzYzIyMjNhNGVlMjA0IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL29wdGltaXplVGltZVNhbXBsZXMubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImNhZjYwOGExNmQzZWRkYWIzMjUzYzJkNDZkN2NlYjFjMDM0YWI3ZjI0ZGI4MTc2MzdjYmQ2Yzc4ODM4ZTA3MjkiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvb3JnYW5pemVQcm90b3R5cGVzLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI3ZmFmNzRiMzhiYzFiOTZiYzVjOWRmNTgzZWZiNDdhNTRkMjg2ZTM4YTc2NDdmMWU3YzE3ZTMxZTc5YWM5NWM1IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL3Bpdm90Lm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJhMzU2NzcwZTdiMzgzMTgwMTQyOTM5MTE3MjFlZmNhN2NiYmNmNjQ5Y2RmNjM2OTdiNzEyMjc5MmEyY2JhYzRhIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL3ByaW1pdGl2ZXNUb01lc2hlcy5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZDY1Y2MzNGNiYmEzNDU0YjE5NThiMTEyMTAzNzhiZDRlNjljMjYyZmExOTY4MTdjNzVlNTkyYTgzZmJjNzU5NCIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9wcmludFN0YXRzLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI1ZDYxMWM0NDg0YmI0NjgxYjg2NzBhMGEyMzY5YjYyNjE2OGJjNTg4ODgzNzg1YWE2YzRlNDEyNWY4ZmQwM2FiIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL3Byb2JlLXNuYXBzaG90cy9zby0xMTAuMC40Lmpzb24iLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjkyZTNmNjQ3YjAwODk3ZjRlNTU3MzE4NTc3NWE3MjE3NjIxMmFmMjRjYzI5OTA0MTlhZTQzZjAwZGM3NDVlZGMiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvcHJ1bmVMZWF2ZXMubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImNiN2MyMDRhN2ExMmZlNTIwYWFkNjJjYWQzZDhhZTRiZDlmN2M2ZmZhMDg3YzFkNmZlYjczNzkwMzgzZDMzNWEiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvcHl0aG9uU2NyaXB0Lm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI0NzUxMGVhMWU1ZmNmMzhkZjEwMGM4YjQxNTQwOTBhNTE2OGZhYTdlZmViNjljZmU3MTllNmI5YjA3NmJiYTRjIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL3JlbWVzaE1lc2hlcy5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMmEwNmQ5MDA1NDYwYjBjNzBlMjFlNzRhYTg4NzkwNzliYTdkMWNhYTJmMzMxNmM5MjdjYWU4MDNlNmY2OGIyNyIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9yZW1vdmVBdHRyaWJ1dGVzLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJkZjcxZmI3NGQxZmFlNWIxN2NlNzZlMzYyNmY5MDhkMmMzZjBlNTA4ZTk5NDM5YmRhNjlhNDI1NjRjODgzZWNmIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL3JlbW92ZVByaW1zLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI2ZWE3OGNjOTZlYThlZjc3OGYxZTAzZTA5YTUxZWNiMGFjMmYxMGU1NDY4MjY0NTI4Mzg0YWZlZTMzNzg5ZTczIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL3JlbW92ZVNtYWxsR2VvbWV0cnkubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjY5NWFlODhmMmYwMzI0OWQ5NGExODg0NmFmZDQzMGIzZGRhZDRmMDZiOWEwMWI3MGFjYzI0NzIzZGFhZTJkNzEiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvcmVtb3ZlVW50eXBlZFByaW1zLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJjYzBhYTNhMWVjNzhjYmMyMTJjYmUzMDk5N2ZiN2QzNDc3MDVkODlkMWJiZjkzY2IzZmJiNjU0ZjY4Y2QzZjk1IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL3JlbW92ZVVudXNlZFVWcy5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiODcxODhiYzY2NjQwMjQ3ZjU3NTZhMmYxMjM2YjkwZDYyNzhjOGVkZTNhZWMyNmM0MDZkMzYwMzU0NjRjNmEyNyIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9ydHhNZXNoQ291bnQubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjk2ZDUzNGZlYWQxNTVjOTRjNzczZDUzNmMyMmIwY2ZkYTVhMmY1MzY0ZWM2Mzc0YjYyZDZhNjc2NGJjYTgxNTQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvc2NyaXB0cy9vcGVyYXRpb24tY3VyYXRpb24uc2NoZW1hLmpzb24iLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjUyNjg3Y2Q4NWIwYjAwZTc4Y2IzMDQ0NzcwMmIyMGRmODhlZWMxMDVmZDdhN2I4NzdiZWM5NzE5OTBjMGQ4YzciCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvc2NyaXB0cy9vcGVyYXRpb24tbWFuaWZlc3Quc2NoZW1hLmpzb24iLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjJjOTdiNGNlMDkyMDYzNTk1MmZiYTNiYTRmMmMzZWE5ZmJjZTJhNmRlNTkxNmNmOGJjMjE1YjNkNTU4ZmIxNDUiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvc2hyaW5rd3JhcC5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMjQwNmJlY2Q5ZjFjOTMyOWNhOWQ0YmRlZDM5ZDA2NjI4NDc3YjNkMWQ2ZGE5MzYwMDdjM2QyYTlkMjk3M2MxZiIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9zcGFyc2VNZXNoZXMubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjZiOGM1MTU3NzM2MTA4OTRhNDBiYWI0N2I3NTAyOTdhYmE5NzgxZjRkNDNmZjg2MTZhNWE2YjE5MWFjMTdiMmMiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvc3BsaXRNZXNoZXMubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjUyNWM3MGQ1MDA0ZjA1MDFhOWU1MDc5ZWY4NzBlOWMzNWQ0MjdlZGM1Njg4YWE2Njc2NWIyMDQ2MTU2YWEzOTIiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvc3ViZGl2aWRlTWVzaGVzLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIyN2JhMmY1NzUxYTA0YTNiYmNiNWUyYTAwZWJjZjcxOGYxOWUyZTAwOWFjN2ExYzI4YzYzNjRmYTVkOWMyYTUwIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL3RyaWFuZ3VsYXRlTWVzaGVzLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI1MmM4NGE4MjZkNmVlMjcyMDg1OTY3NTQ5NWZjOGUyMzBhNjM3ZmRiN2NkODBlZjk0MGRmZjIyNTc0N2EyZDkzIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL3V0aWxpdHlGdW5jdGlvbi5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZWQ0NWI1ZTA4ZGEzZWI5NmIwYjI4N2JjMjgyYTg4ZjVhZjg4YjA2NjJmNmQ2NWU0YTg2MTU3YTczOWI4MjA5YSIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3B0aW1pemF0aW9uLXJlcG9ydC9SRUFETUUubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjE5NzVhNGI5MjBlMDBjNjQyZTA3ZDg1YWM1MDc5NzIwNGJhNzc2YTVlYmZkMGI2NWRjOGZmNDI4YThkNDYyMjciCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wdGltaXphdGlvbi1yZXBvcnQvcmVmZXJlbmNlcy9vcHRpbWl6YXRpb24tcmVwb3J0LXRlbXBsYXRlLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIwZjZmMjliYzQ0ZmM1YTk1MGU4Y2RlZTdhODNlM2FiMDkwYmFmZmZiZDAzY2ZkYWVlYWFjZGI2OGI4NjJkZTkwIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcHRpbWl6YXRpb24tcmVwb3J0L3NjcmlwdHMvb3B0aW1pemF0aW9uLXJlcG9ydC5zY2hlbWEuanNvbiIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMzI1ODg3ZDYyMzE5MTQ2ZDRjZDJlNjM1YjliMDY5OTI3NGMwZThmMmM0NjRmNmJkY2VjOWZhOGEwZmI0MzBjOSIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3V0cHV0LXdvcmtzcGFjZS5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiODk5NzFmMWQxOTQ1MmYwYjM2YmFmMWIyMTFjMjZmOTQxNjQzMDQzOTFmMjZmM2NiMGRiYjIyZGZiZTI1MDM1YyIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvcHJvZmlsZS1zdGFnZS9SRUFETUUubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImVlYjllYjBjNjY4ZjJlYTc3ZTZiYjRlMTFkOTkxNzcxYTAwZGU3N2YyOTk5NDljNzVmMzM0MjA3NjEyOTVjNjciCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3JlcG9ydC10ZW1wbGF0ZXMvUkVBRE1FLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI0N2EyNjQ3MmQxMmE0NDdjNzNkMWIxMDRjMDY5ZWU1NGU0YjM0MjFlMWY0NzA3MGViYTBlNDhlNGY2NDQ1ZjIzIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9yZXBvcnQtdGVtcGxhdGVzL29wdGltaXphdGlvbi1yZXBvcnQuZGVzaWduLWZpeHR1cmUuanNvbiIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZDMzMWVlYzczNTM5OGQ0NDVkMDRkNTE3NDUyODhlYmE1NzYxNDU1OTk2MTgyYTQ0MDU3NzhmYjk0YWEyYzk2ZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvcmVwb3J0LXRlbXBsYXRlcy9vcHRpbWl6YXRpb24tcmVwb3J0Lmh0bWwudGVtcGxhdGUiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjJlNGVhZGJjMTUxM2U0NTJmMWQ0NWU1ZmNkZjJjMDJmY2Q1ZGNkNTUxMzJhZDQ5Mjc5YTVhODk5YTI1ZTdiOTUiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3JlcG9ydC10ZW1wbGF0ZXMvb3B0aW1pemF0aW9uLXJlcG9ydC5tZC50ZW1wbGF0ZSIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZTlmNDQyZDg2ZGZhY2UwNDMwZjg3NTIwZDQ1NmMxYmRiZDY5ZGQyNjg4NGFiN2QyOGE5ZDQwMTdlMDVlYzU2NiIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvcmVwb3J0LXRlbXBsYXRlcy9yZW5kZXJfcHJldmlldy5weSIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiYjEzZWZjNGIxMjBlMDllZjU4ZjIxYTU3NmI0OWUwNDMwMzkzOTBlZTc5YmUzYTgxYTdkZDg0NTExNDYwYzA5NCIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvcnVudGltZS1hcnRpZmFjdC10b2tlbi1idWRnZXQubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjQyY2E1OTM0NjFjY2JkZDljZGJlNzY4NGFmNGRlZWE1MzA1OGFjNWMxODliZTg5M2EwMzgzMzg2MDMyMGNiODgiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3NldHVwLXVzZC1wZXJmb3JtYW5jZS10dW5pbmcvUkVBRE1FLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI2YjRjZTJhMGNlMjRlZDdlOThlNzEzNGIxZjE1OTY5YzE0MDRiN2Q4ODhjZWU4MWNmMDcyOTcwM2RiMzMxZjAyIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zZXR1cC11c2QtcGVyZm9ybWFuY2UtdHVuaW5nL3JlZmVyZW5jZXMvaW5zdGFsbC1hc3NldC12YWxpZGF0b3Itc3RhbmRhbG9uZS9SRUFETUUubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjlkZDAyMGM4YTg1NWVhOWQyYmNkZjE2YWVjMTg4MzBmYTI1YWRkNGM3YTg1MWY4Y2E0YjQ3NGFhMjEyMmJlNDYiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3NldHVwLXVzZC1wZXJmb3JtYW5jZS10dW5pbmcvcmVmZXJlbmNlcy9pbnN0YWxsLWtpdC9SRUFETUUubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjM1NzQ0ODJiMmE1MDM3NmMxNGU2NGJlMzU5MzRjMzJjYWVlOTNlZjQyYWNiMzEwZmI4MjVhZjAxZmZlZTk4ZDciCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3NldHVwLXVzZC1wZXJmb3JtYW5jZS10dW5pbmcvcmVmZXJlbmNlcy9pbnN0YWxsLXNvLXN0YW5kYWxvbmUvUkVBRE1FLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJmYmFiN2Q3MTY0OTBhZmRlZDExZmNmNmYyZGMyMWQyMzA0ODQ3NTVkMjFlNjMwZDVmYzM5MTBlMjQxMWZkZjU2IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zZXR1cC11c2QtcGVyZm9ybWFuY2UtdHVuaW5nL3JlZmVyZW5jZXMvaW5zdGFsbC1zby12aWEta2l0L1JFQURNRS5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiOGJhMjViZDFmNTYyMGFkNzdhNTkxZDg4NjYzYWM0MDJhZjgwMDAwYWI3OTQ0YWJhNDljZmU4ZWE4NmQ0MGRmMyIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvc2V0dXAtdXNkLXBlcmZvcm1hbmNlLXR1bmluZy9yZWZlcmVuY2VzL2tpdC1kaXNjb3ZlcnkubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImRkYmZhYzAwN2ZkMTVmNzUyYjA2YTY5NTUyNzMwNGVjYTE4N2EwOTEzMzQzYWY4NmNlZmU2NWJiYTNmNDRhZDUiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3NldHVwLXVzZC1wZXJmb3JtYW5jZS10dW5pbmcvcmVmZXJlbmNlcy9ydW50aW1lLWNvbnRleHQtaGVhZGVyLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI5NWI0MGE0NThiZjY1MGUyMTJlYjMwMDU2MzNlMjFhZWQwOGE4N2UyYjgzMjA2ZDAzOTc1YTRkZTAzZWQ2OWM1IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zZXR1cC11c2QtcGVyZm9ybWFuY2UtdHVuaW5nL3JlZmVyZW5jZXMvcnVudGltZS1wcm9iZS5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNDFjODVmNjFiYmU1M2Y0OTQ3MjAxYjczZGQzYjZkOGIwMTRhZjNlODAxYjRkNzRhMjY2MzA5MzQyN2I1YzE2ZiIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvc2V0dXAtdXNkLXBlcmZvcm1hbmNlLXR1bmluZy9yZWZlcmVuY2VzL3N0YW5kYWxvbmUtcnVudGltZS5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMTJiZDQ2ZGQzYjYyYTJhMTQ1MGM3MGQ1MTM0OGVmMjk0NDUxYWFhYzM1OWIwYTVhZmQwOTk2Y2NkMmRkZjY2OSIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvc2V0dXAtdXNkLXBlcmZvcm1hbmNlLXR1bmluZy9zY3JpcHRzL3Byb2JlLXNuYXBzaG90LnNjaGVtYS5qc29uIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIzODNlYzIyN2NmMjc4NjI2ZGIzODIwMjkyMmIxNGZmNjY1YWE5ZDQxZGM0YWRiNjI1ZjA3NTVlY2M2OGIzMTVmIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zZXR1cC11c2QtcGVyZm9ybWFuY2UtdHVuaW5nL3NjcmlwdHMvc2V0dXAtcHJlZmxpZ2h0LnNjaGVtYS5qc29uIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIxMjA3Y2Q4YTNmMjhjN2ZkYTlmNTY4NGUzOTJhNGMxMDFmZjYwYmZiMjRmMWVjYzcxODJjMmJjNDc4NmE4M2Q1IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9za2lsbC1tYXAubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImVmN2QxNGNmMzQ4NmVlNTJmM2RmZGFiNTdmZTlkYWJjN2U2NTI5NmU2ZGZlNDk5ZTQwY2YyOTFhMmUzMmU4ZTEiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3NvLXJ1bi1vcGVyYXRpb25zL1JFQURNRS5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMzYwMzgxOWFlM2I0OGM4MGM1MGUyY2IyODBjODRhNjA2N2MyMWRkOTU3MjVlZDM1ZjFmY2ZiZjE0MTE1OTFlNiIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvc28tcnVuLW9wZXJhdGlvbnMvcmVmZXJlbmNlcy9iYXRjaC1tb2RlLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIyMmNlM2IwOTE2MmY3NjIzNTY2NzEwZGVhNGRhZmZjMzEyYjQwNzg3OTA5ZWJlYTNiZjJjOTU5YTUxYzQyNDI1IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zby1ydW4tb3BlcmF0aW9ucy9yZWZlcmVuY2VzL2NvbmZpZy1mcm9tLWV2aWRlbmNlLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJhMmI1MGZjYjNkMWExNjE4NjcwOGI0NDRjMDNlZjg5MWIzNTY3ZjI4ZWQzMWVmMmNkNjRiZWExZTRiNGM4YWUyIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zby1ydW4tb3BlcmF0aW9ucy9yZWZlcmVuY2VzL2ludm9jYXRpb24ubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjU3YWYwYzdhNWYwZmQ2N2VkYjYxZDgzMjg1MWYyOWVjZWFhYjNiNDkyMjE0MTlkMjFlNjMwZmI1ZDVhMDYwNmUiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3NvLXJ1bi1vcGVyYXRpb25zL3JlZmVyZW5jZXMvb3BlcmF0aW9uLXNhZmV0eS5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZTNkNWQwMGM0YTNlYmMxNjk3NTI3MjJiMDZlNzMxNjUyOWY5NTQ3NjBhNmMxZWQ3MGNlMzc5ZGZlOWIyMzcwNSIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvc28tcnVuLW9wZXJhdGlvbnMvcmVmZXJlbmNlcy9waXBlbGluZXMubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImY3MDQ0MjY2NmUxZjY0YzExZWMxNjRjZDYyNmJlOTJkODY1YWQ4NmUxODdhZDk0NjI4MDI3MTFmYzg3N2Y3YWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3NvLXJ1bi1vcGVyYXRpb25zL3JlZmVyZW5jZXMvc28tY3JlYXRlLXByb3h5L1JFQURNRS5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZGI0YjJkM2JhNjI3Nzk4MGMyNmY1ZWIwMTI4YTVhNmRhODRkMGMxMDdkMGQ1NDljZjkyZTNjNTQ2Y2VhOGEwYiIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvc28tcnVuLW9wZXJhdGlvbnMvcmVmZXJlbmNlcy9zby1jcmVhdGUtcHJveHkvcmVmZXJlbmNlcy9ib3VuZGluZy1ib3gtcHJveHktbW9kZXMubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImViMmQzMDRlZGZmMmJlZTcxMzFiMjJjZDBhYzc3NzM3ZTM1N2QxNTY5ZmQzYTFkOTlmMmQyZjY0YTlkY2QwZGUiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3NvLXJ1bi1vcGVyYXRpb25zL3JlZmVyZW5jZXMvc28tY3JlYXRlLXByb3h5L3JlZmVyZW5jZXMvZGVjaW1hdGUtc3RlcC1yZWNpcGVzLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI1N2VlOGRlOTQ1MzJhNzg5OTJlYjUxNzkwNjdlMjM0MTk2M2EzNmI5YjM1YzZmMWU0ZjFiOWNiMDNjZTRmNmZmIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zby1ydW4tb3BlcmF0aW9ucy9yZWZlcmVuY2VzL3NvLWNyZWF0ZS1wcm94eS9yZWZlcmVuY2VzL2RlY2ltYXRpb24tdHVuaW5nLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIzNzk3NzY4MjU1ZjkyYjU1NTA5YzAyYTAzMTc4YWFiZWE0OGZhZjdkNWY3OWIzZjBhM2Q5OTFmNDgzMDRmODIyIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zby1ydW4tb3BlcmF0aW9ucy9yZWZlcmVuY2VzL3NvLWNyZWF0ZS1wcm94eS9yZWZlcmVuY2VzL3Byb3h5LWNvbmZpZy1yZWNpcGVzLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI4NjRmYWQ2YmVjYTVlZWRiMTc3ZjQ1MGJjZGIxMGY5NjM3MDQxMzdkZTkzOTdmZjVmNmM2YzlmZmJhNjMxYzFiIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zby1ydW4tb3BlcmF0aW9ucy9yZWZlcmVuY2VzL3VuaXRzLWFuZC10b2xlcmFuY2VzLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI5NjBjNGIwODVlMWQ4NzM3MWMwNjg5ZTVjZjQ3NzJhMjgzODEzZmFmZGU0ZGVhOGM2NjMyZjkxZjVlYzUwMzc4IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91cHN0cmVhbXMvdXNkLW9wdGltaXplLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI5MWFkZGFhYjg1OTMwODk2NzQxNDkwYzMwZjc5YzJkNGFkMGU2OTUwZTQzZGNmMzBkN2QwZjAwMmNkMDQ2NjZkIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvUkVBRE1FLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJkODJhZWQzYmNlZDEwMTAwMjFkNjg5ZDNlNWUyYjA5NDg2NzhhYjg4OTk3M2NlYTRiYTgyOGZjZWYxODQ0MmI5IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvcmVmZXJlbmNlcy9hcHBseS1yZXN0cnVjdHVyZS9SRUFETUUubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImQzY2VlNTMyMDJkYzFhYWU3ZWQwNzFlODQwNDAyZmQzNjJmMzZjOWI1NzBjNDdlZmM0YzM3ZmQyZDhiNTM1NWEiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC1zdHJ1Y3R1cmUtYXNzZXNzbWVudC9yZWZlcmVuY2VzL2FwcGx5LXJlc3RydWN0dXJlL3JlZmVyZW5jZXMvaGllcmFyY2h5LWRlZHVwZS1yZXdyaXRlLXRvb2wtc3BlYy5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNzUzMDE5MGM5MDc0YzQwMjkwOGE2YzhhMzAyNzRlODg0YTU0MzdkNmM4MmJiMjc1MDhiZmM0YThkMGUwNzI1MiIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXN0cnVjdHVyZS1hc3Nlc3NtZW50L3JlZmVyZW5jZXMvYXBwbHktcmVzdHJ1Y3R1cmUvcmVmZXJlbmNlcy9yZWYtcmVtYXAtbW9kZS5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZDQyZjUwZTA1MDI0MDkyYjdjM2MzMzNkMzZjYWI5OGMxZDA1MGE3NzAxNWJjMzc0OWE4ZDE0MjUwN2M5YjBhMSIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXN0cnVjdHVyZS1hc3Nlc3NtZW50L3JlZmVyZW5jZXMvYXBwbHktcmVzdHJ1Y3R1cmUvcmVmZXJlbmNlcy9yZXN0cnVjdHVyZS1tb2RlLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJlMzc2ODY3YTIxMTI0NWVkZDliOTVlN2IwMzZkMzFlODdkOWRlNzY2YWNmODBiZWFlN2E5ZTNmNTM3ZGU5ZDc1IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvcmVmZXJlbmNlcy9hc3NldC1zdHJ1Y3R1cmUtcHJpbmNpcGxlcy5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMTIwNjFhODE2ZGIyMTdlMDZhYjhkMGVlNWIyNWFkYTk5YmYxNDFiY2NhYWUxMTZiZDNkMmM2NTRmMWQ3ZmU0NiIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXN0cnVjdHVyZS1hc3Nlc3NtZW50L3JlZmVyZW5jZXMvY29tcG9zaXRpb24tYXVkaXQubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImJjMGJjZTkxMjkzZjkxN2VkYmRmNDZkYWVmMzNmOGNmNjdmYjZkZTc4MWFlNzhiM2E1YmRiMWM4NDdmMTUxZDgiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC1zdHJ1Y3R1cmUtYXNzZXNzbWVudC9yZWZlcmVuY2VzL2ZhY3RvcnktbGV2ZWwtc3RydWN0dXJpbmcubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjI2MjFhNjlhM2ViY2VhNGVlYWY5ZGI3YzMyZWYwNGNjM2M2MjVlMGQwMDU3MDRhMjU0N2E1MjRjZDZiOTU0YTUiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC1zdHJ1Y3R1cmUtYXNzZXNzbWVudC9yZWZlcmVuY2VzL2luc3RhbmNpbmctcmVhZGluZXNzL1JFQURNRS5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNWIzOTNlZjAyMTNkZWFiZjNhNTdkZmM2MjdjZDkwMTNlNWEyOGY2YmIxMTMyOGQxMTBmYTc3MzY4Y2IxOWY2MiIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXN0cnVjdHVyZS1hc3Nlc3NtZW50L3JlZmVyZW5jZXMvaW5zdGFuY2luZy1yZWFkaW5lc3MvcmVmZXJlbmNlcy9pbnN0YW5jaW5nLWd1aWRlLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI1YzJjNWMzNWQ3ZWI5YjYyMzQxMjZjZTQzMGQwOTIwZmNmODI3MDkxNGM3OTQ5MTY5NWE3N2UzZGQzMzYwYmNkIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvcmVmZXJlbmNlcy9pbnN0YW5jaW5nLXJlYWRpbmVzcy9yZWZlcmVuY2VzL2luc3RhbmNpbmctdHJhZGVvZmZzLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIzMDdlYzM5N2FjOGExNmQ4MGFjNjkxMGIzZWQyMDIxMTg3N2ZiMzQyYjc4YjVhMWE0NGMwZjNlOWU4Mzg1Y2I3IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvcmVmZXJlbmNlcy9sYXllci1oZWFsdGgubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjgzYWM0ZWNjY2YxYjVjZDc3ZDhkZDljMDVkMWRiMjYyNTE3NDE1ZWFlNDQ5MzMwNDNjY2MzYzJmNjdlYTcxZDAiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC1zdHJ1Y3R1cmUtYXNzZXNzbWVudC9yZWZlcmVuY2VzL29wdGltaXphdGlvbi10cmFkZW9mZnMubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjNiYzlhNDQ4NmMxZGM1NzRmZjVmMzZjMmE0YmI0MmU5NTkyYjRlMjZjZTU5N2RhNDc4NTI3NjI1ODMwYmEyOWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC1zdHJ1Y3R1cmUtYXNzZXNzbWVudC9yZWZlcmVuY2VzL3Jlc3RydWN0dXJlLWRlY2lzaW9uL1JFQURNRS5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZWU0ODk3MTg2YzQwNDIxMTJlOTcxMGE2M2U2ZTcyNzMzZmRlNDU4ZjZiMjAzZTJkNWE0Y2MzZTJjYmVkMmMxMCIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXN0cnVjdHVyZS1hc3Nlc3NtZW50L3JlZmVyZW5jZXMvdXNkLWVkaXQtdGFyZ2V0LXBsYW5uZXIvUkVBRE1FLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI2NGRmMmE5NjAwOGFjYzJiODRhOTgwY2U1Y2ZkY2Q3OWY2MDJjNTIxYTBmYWVhZGYxYmMzNWZkMWFhZjhiNDRkIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvcmVmZXJlbmNlcy91c2QtZWRpdC10YXJnZXQtcGxhbm5lci9yZWZlcmVuY2VzL291dHB1dC1zYXZpbmcubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjVjMDFlM2VhYTUzNmNmMTFiMzA3MzQwOTE2YTk5ZDJiNTUzMzYzNTk0MGJiMTI0NjU3Y2Y2OGZlNmU4MmZhNmQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC1zdHJ1Y3R1cmUtYXNzZXNzbWVudC9yZWZlcmVuY2VzL3VzZC1lZGl0LXRhcmdldC1wbGFubmVyL3JlZmVyZW5jZXMvdmFyaWFudHMtcGF5bG9hZHMubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImViMjIxMWIzMjM0MThhZjM3YmU4ZWEyMDc2M2Q5YmE0YjdkZTYxNTYyZjgzOTVhYzY4M2Y5OGRlMzE4NjA1OWMiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC1zdHJ1Y3R1cmUtYXNzZXNzbWVudC9yZWZlcmVuY2VzL3VzZC1oaWVyYXJjaHktZGVkdXBlLWNhbmRpZGF0ZXMvUkVBRE1FLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIzMTg0Y2IzNzBlYjcwNDNlNjFlNDYzN2ZmNGY3MGJkODlkMmEyMjhhNjM1ZTM2YWQ4ZjNkNzI5ZmQ4NGY4NmQ1IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvcmVmZXJlbmNlcy91c2QtaGllcmFyY2h5LWRlZHVwZS1jYW5kaWRhdGVzL3JlZmVyZW5jZXMvaW5zdGFuY2UtY2FuZGlkYXRlLWZpbmRlci1zcGVjLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJmMTdmNTRhNTA4NTMzN2Q3MTU1ZWFmMjhlMDc1Yzc5YjQ4MWZmMmE0Y2Y0NGEzOTBiOGNkYzQ0YzkzOWMxNmY0IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvc2NyaXB0cy9hdWRpdC1yZXBvcnQuc2NoZW1hLmpzb24iLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImZmZjc5MDAxMTM5NDM1YTJmNWVmY2RkZTg5YjI5YzQ1NzViZWFkNTk0ZTMxNmNlY2ZkMmVkOWRiMTExOTUwMmIiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC1zdHJ1Y3R1cmUtYXNzZXNzbWVudC9zY3JpcHRzL29wdGltaXphdGlvbi1wbGFuLnNjaGVtYS5qc29uIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIxYzhmODljNTAxZjI3MTQ0YjE4YzRmNmFmM2JhMzFjZjVjNzc5YTE2Y2EyMmI5NTQ5MmU1NTI2MjRjM2FkZmM1IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvc2NyaXB0cy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQtcmVwb3J0LnNjaGVtYS5qc29uIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIwMmU0NzhiZWI2YmQyNzdhNTYyNjZkZjJhNjZiODlkNjNkMTYxZGMyZjBiNGZlZTUxOTJlYTliYmVlYzY1NTE5IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2QtdmFsaWRhdGlvbi1ydW5uZXIvUkVBRE1FLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJkYjMxNWFhYWU5MTU3ZWIwYmQ5ZGIyZThmMjIwZGZhZDg2NjNiMWJhZmZkMmY3ZTk2YTk3MDVkM2YzMzlkYjJlIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2QtdmFsaWRhdGlvbi1ydW5uZXIvcmVmZXJlbmNlcy9zby1pbnRlcnByZXQtdmFsaWRhdG9ycy9SRUFETUUubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjg2YThiYmIyMTBiMDVlYmU0MDk0OWUyY2NkN2E0NzUxMmM1MjY4YzRhM2JhNDZiYTkyZjgwMTg0YTU2OTExMTkiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC12YWxpZGF0aW9uLXJ1bm5lci9yZWZlcmVuY2VzL3NvLWludGVycHJldC12YWxpZGF0b3JzL3JlZmVyZW5jZXMvZm9sbG93LXVwLXF1ZXJpZXMubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjM3ZWRhZDQ3MjZiZWQzOWFmZjQ5N2EzMTZlZTA1NTZjZmMwYzI0ZTcwNTQ3M2MzZDVhZDU3ZmE2YTU0ODA1NDEiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC12YWxpZGF0aW9uLXJ1bm5lci9yZWZlcmVuY2VzL3NvLWludGVycHJldC12YWxpZGF0b3JzL3JlZmVyZW5jZXMvcnVsZS1yZWZlcmVuY2UubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjllNmE4NzY0MWRlZmY3ZDU0ZDgzNDEzZWY3NTNmNTViN2I2NzI2YTM4YTdkMzIxN2RiYTk2NDU1ZGNiNDFjZWYiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC12YWxpZGF0aW9uLXJ1bm5lci9yZWZlcmVuY2VzL3NvLXJ1bi12YWxpZGF0b3JzL1JFQURNRS5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNGY1MTJhZDkyNzQ1MDJiZjhkZWVjYmM1Mzk5N2E5NTc4MDlkNzg0MWMxMmMyNmUxMWE0NzRhZDg1MTEyNWRjYiIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXZhbGlkYXRpb24tcnVubmVyL3JlZmVyZW5jZXMvc28tcnVuLXZhbGlkYXRvcnMvcmVmZXJlbmNlcy9pbmZyYXN0cnVjdHVyZS5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMmU3NDQ1MzY4ODczNWVjMjM0ZmJjNWQ3YTkyOTY2MjkwM2M5NWU5MTdjOTFhOWY4ZjhkMGU0NjQ3ZjMzYjcyYiIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXZhbGlkYXRpb24tcnVubmVyL3JlZmVyZW5jZXMvdmFsaWRhdGUtdXNkLWFzc2V0LXZhbGlkYXRvci5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiYzI4YjY1YWY3NjZmOTEwZTJiMWVmNjlmMzNkYThlMGUzMjExYWUzYzFkNDU0NzE1NmM3Mjg4MWNhZTM4YmU2NyIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXZhbGlkYXRpb24tcnVubmVyL3JlZmVyZW5jZXMvdmFsaWRhdGlvbi1zY29waW5nLm1kIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJiNGI0ODFlYWVlNTg0YWQyMjIyYTcwNTNjODIxNjY0ZjZhMjY5ZjYzMThiYmU3MGJkZTBmZTE5NzA4NGQ2NDI4IgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2QtdmFsaWRhdGlvbi1ydW5uZXIvc2NyaXB0cy9hc3NldC12YWxpZGF0b3ItdmFsaWRhdGlvbi1yZXBvcnQuc2NoZW1hLmpzb24iLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImZmODY0NzM2NWI3ZTExNDIwMTg5NTFkMTViOTdhNzM2MDk1OWY5OWY4ZmExYjE0YjAxYWYxZTQ0N2FlNTk1MGEiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC12YWxpZGF0aW9uLXJ1bm5lci9zY3JpcHRzL3ZhbGlkYXRpb24tcmVwb3J0LnNjaGVtYS5qc29uIiwKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJlODFiYWQ3MzQ0MjBiYTUxYmVjOTU4YmRlN2RjNzhkYThkNzBkZDgxYTJiNTRjNjBlMGI1YThhMmIzNTk0NDAxIgogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy93b3JrZmxvdy5tZCIsCiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiODc1MTI2NmE0N2E1NmYzODY5M2I4ZmE5YWI3MmRmMmEwNjI2ZTM2MWViY2UzN2JmM2ZmNzY4NWMzZGUxMzU2OSIKICAgICAgfSwKICAgICAgewogICAgICAgICJuYW1lIjogInNraWxsLWNhcmQubWQiLAogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImI3MDNlMmQzZDNjYzUzMGViNGMwYWE3MDgwYmMwZTJiN2M4NzMzZDlhNDNiYjZmY2Q1ZDdkZGE4OTI4NzBlMTEiCiAgICAgIH0KICAgIF0KICB9Cn0=","payloadType":"application/vnd.in-toto+json","signatures":[{"sig":"MGUCMGx4a/8YrTh+yy0qP2wsu7RZmN6jW9fXEQc52nfauFul/d/PDqeBjD+j43Peqhmt9AIxAPxPEo+6vSPNuvRmbTs8pcKC+V9b9TRn/XiEYZQXpNKds3FhYML2qO7cl/G9tRYKCw==","keyid":""}]}} \ No newline at end of file +{"mediaType":"application/vnd.dev.sigstore.bundle.v0.3+json","verificationMaterial":{"x509CertificateChain":{"certificates":[{"rawBytes":"MIICgzCCAgmgAwIBAgIUKIyS7SxNteQIiWzK1dWj85E6520wCgYIKoZIzj0EAwMwVTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjEpMCcGA1UEAwwgTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBJQ0EgMDEwHhcNMjYwNDAxMDAwMDAwWhcNMjgwNDIyMTUzMzA5WjBUMQswCQYDVQQGEwJVUzEbMBkGA1UECgwSTlZJRElBIENvcnBvcmF0aW9uMSgwJgYDVQQDDB9OVklESUEgQWdlbnQgU2tpbGxzIFNpZ25pbmcgMDAxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEYoRM9bQl/dGlwSRNi6bTpIJUXH8Nv9GciP6LSflJYYMLCc296kpyuTSsk5ddbAWiDcFX3C/ydX3jwc+qCLYP6uHy9XphyLjOQ27Yb2J6rBLVtRBS1mgGco/Gr7fL6ODco4GaMIGXMB0GA1UdDgQWBBRQ/5ZW3nJ6lmo9SVk7I15o7UGmpTAfBgNVHSMEGDAWgBRPGpILxMBBleJSsBGjrMKsby1CgjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9vY3NwLm5kaXMubnZpZGlhLmNvbTAKBggqhkjOPQQDAwNoADBlAjAUygu/GiOCIXrgGr4SmLgeEVDcEitfFUv7ALbvLVGVyMysB3mxmO/uInZfXzWcJZsCMQDxuoxj4ZmO30jhkPIcCxGFCOvnUsnfU3TfGcouYm4M6iRpbKvtVnHPiy4bi6pcKf0="},{"rawBytes":"MIICiDCCAg6gAwIBAgIUZsIuSv9NkpJCNqtYEfCouVv5BzowCgYIKoZIzj0EAwMwUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTAgFw0yNjA0MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowVTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjEpMCcGA1UEAwwgTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBJQ0EgMDEwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASI72cR3ctKGg4VWnB3bNja6g1Z2PnOmFEopkPof+QeIcPk9rT+g9MjJnq51EQXL93a7C2GJ9J985G4o2V85VD7wJ1RaXhluHW2rf3y8bQGeAYaKMr5s/hUgn+M3/9WlWejgaAwgZ0wHQYDVR0OBBYEFE8akgvEwEGV4lKwEaOswqxvLUKCMB8GA1UdIwQYMBaAFItnoAjjfuCEUvzyvWyI2vOGvwPjMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL29jc3AubmRpcy5udmlkaWEuY29tMAoGCCqGSM49BAMDA2gAMGUCMQCeIMMfAbyzPDacw2MxG+Yt1cikrJX/DVxiGfXuHmkkXn6VgSzE79+lkqDErpVO2gYCMCNEColOyvUvkzZGUEI1hQ3PfMgi3FIo9tHoBKMw4/wGBLFpu/0ubtmbBXM6/UMOEw=="},{"rawBytes":"MIICRTCCAcygAwIBAgIUeJdY3rV86EdvFmG7L8LJBsyQFYkwCgYIKoZIzj0EAwMwUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTAgFw0yNjA0MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowUTELMAkGA1UEBhMCVVMxGzAZBgNVBAoMEk5WSURJQSBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcTlZJRElBIEFnZW50IENhcGFiaWxpdGllcyBDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IABAYpiXCDjJ9NT2eSDhyHJVSw1Tbze18cGG2F/578oWvHxg23eQAhNRYdq88i1iOshZSO6C29doKui5Xpmo/7Ctw9Sx4PP2RzOmIuOLCuTdNtKcTRwi4GEsd5BAFvWj42M6NjMGEwHQYDVR0OBBYEFItnoAjjfuCEUvzyvWyI2vOGvwPjMB8GA1UdIwQYMBaAFItnoAjjfuCEUvzyvWyI2vOGvwPjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2cAMGQCMCwtAjWLaNwgGWNCgdyNoTyvNhqWRECRJV2r3+7w8g0PL6NHLOsbkgE09BH95h8XlgIwTaQmbbUh2ChAJ5TA1wRiVDnCcvbzHlZl2jM2FcwQQZlk19LOAbyGMRixbu2Ww/rj"}]},"tlogEntries":[]},"dsseEnvelope":{"payload":"ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YxIiwKICAic3ViamVjdCI6IFsKICAgIHsKICAgICAgIm5hbWUiOiAib21uaXZlcnNlLXVzZC1wZXJmb3JtYW5jZS10dW5pbmciLAogICAgICAiZGlnZXN0IjogewogICAgICAgICJzaGEyNTYiOiAiZTgzODkyYjA2ZjFiNDJkMGMxNjE5ZDYwMzMxNjMwMmIyMWQ3ZmNmNzVmODdiYWM3MDdiOGI2OWI2YjFkNjNlYSIKICAgICAgfQogICAgfQogIF0sCiAgInByZWRpY2F0ZVR5cGUiOiAiaHR0cHM6Ly9tb2RlbF9zaWduaW5nL3NpZ25hdHVyZS92MS4wIiwKICAicHJlZGljYXRlIjogewogICAgInJlc291cmNlcyI6IFsKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImQ3N2QzMTI5YWViZDY1NTk4OTZlODUxNjEyOTdhYWQ2NDlmMWJiZmM0ZTJiODc3NGUwZTZkYzgwNWFkMTE1MmMiLAogICAgICAgICJuYW1lIjogIkJFTkNITUFSSy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjhhNzFiMDIzZWU3ZDk5YzFkNWIwNTczMTllM2I2OWY5MGUwMzg0MWMzZTliNTZkNTI1YjMzNmY5YmIyZTI2MTgiLAogICAgICAgICJuYW1lIjogIlNLSUxMLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMDMxMjEzY2JiOTVmYzBkMWExZWUzNzczOTYyNzEzNjdmMjM0YzgzYWZmNjNhNWVkNWUwNTYyZjE3M2ZhODU3YiIsCiAgICAgICAgIm5hbWUiOiAiYWdlbnRzL29wZW5haS55YW1sIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiOTg3N2E1YmM4NTVlY2YxMjNjMTVhODQ2YzFmZjIyMWYyMzM2NTJkODEwMmZkY2RjYTVhNjM4ZDI4YWEyZDRlMSIsCiAgICAgICAgIm5hbWUiOiAiZXZhbHMvZXZhbHMuanNvbiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjM1NWEyYmU3MTIwZTE0NzI5NWM3YmEyZWI1ZDllM2MyNzNiYWZiNDIzNzk3NmE2NzIxYzZjM2JkMjI5YTE0YjQiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvY2FkLWNvbnZlcnNpb24vUkVBRE1FLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiODViZmY2MTYyZTY5N2U5NzkyNTFmMWJiNWJlMmY4N2I0NzE1NzkwZTllNDRlMjZhMmQ5MjllYmJkMmFiM2ZhZSIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9jYWQtY29udmVyc2lvbi9zY3JpcHRzL2NvbnZlcnNpb24tcmVwb3J0LnNjaGVtYS5qc29uIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNDI1OTkyZTgwODBkZWEwOWUxYzRmYTQyYWVlNGJiZDcyZGQ0MTllZWMyZjc1YTAxNjg2NDIzODIxZWViOWIxOSIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9jb21wYXJlLXByb2ZpbGVzL1JFQURNRS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImExZDU4OTU4NDE2NzFmNWYwODNmNzY0MGNhMzNhNDI5NWNlM2QzZWUwNWUzNzRiZDNmYjQwZTk1YzA5ODAwNzEiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvY29tcGFyZS1wcm9maWxlcy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImVlNGRjYjMwNTM0ZGM5NGU3MDljYzI1MTI5Njc0MDkzM2ZiZDE2NzM3NDkwODNhMWQ4NGE4YmJlZWNlN2YyNzEiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb21uaXZlcnNlLWF1dGhlbnRpY2F0aW9uL1JFQURNRS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjJmNjI1YTExMTMxODAxYjFjMDMzNjVhOTNiYmNmN2Q0YTk1Zjk3YjM2NTRhNTQ1ZjM3ZmMwMjUwYjdmZDRiZTAiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9DTEFTU0lGSUNBVElPTi5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjAyZTI2YTc5NzE1MGE1ZWVkOTZjMWE3NzUyMzE1ZTQ5Yzg4MWI5M2U1ODg2MjgyOGY4NWFlZGMwMTVkMWY0ZjUiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9FWEVDVVRJT04ubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJhMTlkNTZlNWI0YjlkNTFlZTY3MWUwZWQ1YmQwNDdhYzc0MWZhN2VhZWQ5ODcxYzBjMDRhOWFhYWZkNWNkMmQ4IiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvUkVBRE1FLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNmJlNmRlYjk0MjM3NjY1NDEwMGQyMWVkZDNmMWVkNWNjMzA5YjA0OWM5N2EyOGE1MzY2ZGM1MDkwNWYwOWYwZiIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL19jdXJhdGlvbi5qc29uIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMjA4ZDEyNmRjNTc5YWJkM2I3NDBhZjVhNjQ4Nzk0NTI5N2M4ZTg0MzFmNjAwMDk5NDc1OTBiOTY1NTI0YjVjYSIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL190ZW1wbGF0ZS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjgwODJhNThiOTMyMzdhYjhjMzNmOTliNTBiOGRmNjM2OTc5NDQ0YWFmYmVhZTZkOGNjNzdkNzM5M2Q2Yjk2MzQiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9ib3hDbGlwLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNTMxMzVmNGVlNzNmZTg1YzllMDFjZGY4OWQ0ZWY1YWQzODhjZWFkOWJlM2VhNmI4N2EzMTdkODdjZWFmYmQ4MyIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL2NvbXB1dGVFeHRlbnRzLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiODY0MjIxZDNlZTVjMjI1YTc5NDFkZmVkNjkyNTQwNzA1N2E3MGJkMGM4YWFjNjU2ZDhjNTQ5MWVmODU1NmM2NiIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL2NvdW50VmVydGljZXMubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJiMTNhNmRhYWE3YjQyYWYwYTkyYjgzZTYzZTZlMzkwOGY4NjBkMjA2NWE4MmQwYWUxYjIzODQ5ODM4MDhiM2RkIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvZGVjaW1hdGVNZXNoZXMubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI0YmQ1Yzc1YTk3YjkxYWExNmUyZWRkOGEwOTQ1MjhkYWI1YWU5OWI1MzkwZGUyYmIyNTIyNmYyZmQ5NTMxZTFjIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvZGVkdXBsaWNhdGVHZW9tZXRyeS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjczMzdjZjdhMDhmY2I5M2Q3YmQ5ZDU2MmI0ODMxNjAwMGIyNTg0MGUxYjc2YmM0Y2MyZDNiNjk5MmI5YTFmMTUiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9kZWR1cGxpY2F0ZUhpZXJhcmNoaWVzLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNTNhZThiNzJmNmIxMGI3ZmE5ZDhiOTA3MGYwODgwNWZkYTY5MTk5YjI1MjRkMjRmNjczZWI2NjZmZTQxMTNhNyIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL2RlbGV0ZUhpZGRlblByaW1zLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZTU1Y2NjNmE3ODgwMWNlNjdlOTk4MTBlN2FjMWI3YTdiODFjM2VlOGY1MTZhYzc2MGE5NTJiYTFkYjU4MmZiNyIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL2RlbGV0ZVByaW1zLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiYzNiMzU4ZjBiMWYwOWY0OTJlMWFjYzI0NWNhMjBjNDRkNGYwMDNjYWE3MzIzZjEwNzVjZjI5MWQxNDVjNDNkOCIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL2RpY2VNZXNoZXMubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJhODNkOGUwMTZmMTA0ZDQxMTY0YzAwYjZlMWQxMmJmY2JkYzEyYTAwOWM4M2U5NzE2MTUyYjAzOWRjMzlkMzlkIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvZWRpdFN0YWdlTWV0cmljcy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImI2NTFkMGM1Yjg3ZDQ2NWU1ZjRlZDBiNzA5ZTZkOWFiZGE5MmY3MjNjNzI4NWQ5MmQyZWM1NzJjOTI0ZjBlNDkiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9maW5kQ29pbmNpZGluZ0dlb21ldHJ5Lm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiM2RhNDM5MjFkNGM5YjYxMjVjOGJiMWIwYTA0NWU0NWM5YzVjMmZlNTQ4NTZkNTBlYWE5MDUxN2U1ZWRhZWZkZiIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL2ZpbmRGbGF0SGllcmFyY2hpZXMubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJjNWYyMTUxMzg5ODNhOTMwNGM1ZGIzMjI0ZjMxNzc3YjkyYTg0ZDQ3NjY1Y2FlYWNiOTQxMjIyODcyMzIyYWFhIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvZmluZE9jY2x1ZGVkTWVzaGVzLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiYWI0MzI0OTcyZThhNGE0ZTEzYjExNGU5MGEyMWYxOTM0MzM3NDIyNzI2YTZkZDBlY2E0M2U2MTAyODYyNDY1OSIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL2ZpbmRPdmVybGFwcGluZ01lc2hlcy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjIwOTNmZjNkZmNmMDVlNmY2MDMzOTY1YjI3YzY0YmZmNDFjOTdiM2RhZjc4M2Q3OGFlYjNiODIyOGU5N2UxM2EiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9maXRQcmltaXRpdmVzLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNzM2NGFjNjA2NjFlODA0NzcyOGZlYjdmNmQxNzQ4Y2I5ZDY0MzUyNTI1ODExNGViZTQ3M2E3ZjNjNjg2YzllNCIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL2ZsYXR0ZW5IaWVyYXJjaHkubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJlN2M0OGUwZGRkODQ3Mzc3OWYxMDUzYmM0NDhhMmJiYWQzZGZhYTVlNWM5ZDE3M2U2MzY3YzM2ZmVkMjk1M2RhIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvZ2VuZXJhdGVBdGxhc1VWcy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImVjOWMxZTA0NTkyMzE5Y2UzMGY3OTNjMWY2NDk1MWRjNDJjZDEzYTljNmY0NjNkM2RjZDM2ZjVjYWFhNjZjMTIiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9nZW5lcmF0ZU5vcm1hbHMubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJkNDg4ZTA1ZGQyODNhOWQ0MGI5MjQ5NzI0ZjQ4MTNhZGI3OTQ1ODYyY2YwOTk1ZDdkOTUxMjhiNTU5ZDc0NDRmIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvZ2VuZXJhdGVQcm9qZWN0aW9uVVZzLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiYjhmODFiZDgzY2EzYWJlZGEzZDAwZmJhZGM3NWMzY2EyZWE0Y2Q0YTBlNGEwMTI4YjA1YTJmMjk3YzY0NThiZSIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL2dlbmVyYXRlU2NlbmUubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI0ZDk0NjRkZGQzNWYzM2E5ZTY3MTNkODFiMTFkY2M0YTliMzgxZjJkNzk1YTRmZmVlMTRmOTZkYTE3OWQzNzkwIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvbWFuaWZlc3QuanNvbiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjQwOTMyZTllNDg5NDJlZWExODZjM2ZmOTZiZmE2OGMxZDIyNTkxYjBkYjVhNDY3ZWJkMDdlYzRmZjQwMjg1OGYiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9tYW5pZm9sZE1lc2hlcy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImZmOWRiNTM5OTNhYzFjMzhkMmZlMTc1ZTRjMGIzNzFjZmRjMzkzMzZlYjc0NGMxYjNhMDBjMmQ0OGRmYzI0NWUiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9tZXJnZS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImE3YWNhMzNjYzg2NmUxNTczMWFkNjI2MGQ0YzZjZjA0MGUxMjAyZDQwZTg3NmQ3MTExOTcxZjM4NjNlNzM5ZTQiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9tZXJnZVZlcnRpY2VzLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiN2RiNWJjMGYxYmM5OTI0MWUxOTI0NTRlYjJjODM5YjViZGU5OTRjNGQ2YmY1YTJkMzgzMDBiMGVlMzNiMDkwNiIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL21lc2hDbGVhbnVwLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNzYzOTEyMDU4ODJkNjUwOWUwYWU0ODk2M2YyZmYyMGQ2ZmYwMTllMzcxNmI2MzIwNWE0ZmRlYzE1YTE4NjFhNCIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL29wdGltaXplTWF0ZXJpYWxzLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNTJmZWZiZGZiZDMwZTMxYjI1MDkwMWI1YzkzMTYxOTNjY2Y1NGY4NzYwNTJjMDMxMzljOGQyYzA0MGUxYWI0YiIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL29wdGltaXplUHJpbXZhcnMubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIzZDBkNGQ1MTc0YjYwNjk3OTUyMTk0M2E2OTVlN2ZhODllZDU0YWIyNjM3MTNjMTY2ZmQzYzIyMjNhNGVlMjA0IiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvb3B0aW1pemVTa2VsUm9vdHMubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJjYWY2MDhhMTZkM2VkZGFiMzI1M2MyZDQ2ZDdjZWIxYzAzNGFiN2YyNGRiODE3NjM3Y2JkNmM3ODgzOGUwNzI5IiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvb3B0aW1pemVUaW1lU2FtcGxlcy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjdmYWY3NGIzOGJjMWI5NmJjNWM5ZGY1ODNlZmI0N2E1NGQyODZlMzhhNzY0N2YxZTdjMTdlMzFlNzlhYzk1YzUiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9vcmdhbml6ZVByb3RvdHlwZXMubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJhMzU2NzcwZTdiMzgzMTgwMTQyOTM5MTE3MjFlZmNhN2NiYmNmNjQ5Y2RmNjM2OTdiNzEyMjc5MmEyY2JhYzRhIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvcGl2b3QubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJkNjVjYzM0Y2JiYTM0NTRiMTk1OGIxMTIxMDM3OGJkNGU2OWMyNjJmYTE5NjgxN2M3NWU1OTJhODNmYmM3NTk0IiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvcHJpbWl0aXZlc1RvTWVzaGVzLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNWQ2MTFjNDQ4NGJiNDY4MWI4NjcwYTBhMjM2OWI2MjYxNjhiYzU4ODg4Mzc4NWFhNmM0ZTQxMjVmOGZkMDNhYiIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL3ByaW50U3RhdHMubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI5MmUzZjY0N2IwMDg5N2Y0ZTU1NzMxODU3NzVhNzIxNzYyMTJhZjI0Y2MyOTkwNDE5YWU0M2YwMGRjNzQ1ZWRjIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvcHJvYmUtc25hcHNob3RzL3NvLTExMC4wLjQuanNvbiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImNiN2MyMDRhN2ExMmZlNTIwYWFkNjJjYWQzZDhhZTRiZDlmN2M2ZmZhMDg3YzFkNmZlYjczNzkwMzgzZDMzNWEiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9wcnVuZUxlYXZlcy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjQ3NTEwZWExZTVmY2YzOGRmMTAwYzhiNDE1NDA5MGE1MTY4ZmFhN2VmZWI2OWNmZTcxOWU2YjliMDc2YmJhNGMiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9weXRob25TY3JpcHQubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIyYTA2ZDkwMDU0NjBiMGM3MGUyMWU3NGFhODg3OTA3OWJhN2QxY2FhMmYzMzE2YzkyN2NhZTgwM2U2ZjY4YjI3IiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvcmVtZXNoTWVzaGVzLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZGY3MWZiNzRkMWZhZTViMTdjZTc2ZTM2MjZmOTA4ZDJjM2YwZTUwOGU5OTQzOWJkYTY5YTQyNTY0Yzg4M2VjZiIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL3JlbW92ZUF0dHJpYnV0ZXMubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI2ZWE3OGNjOTZlYThlZjc3OGYxZTAzZTA5YTUxZWNiMGFjMmYxMGU1NDY4MjY0NTI4Mzg0YWZlZTMzNzg5ZTczIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvcmVtb3ZlUHJpbXMubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI2OTVhZTg4ZjJmMDMyNDlkOTRhMTg4NDZhZmQ0MzBiM2RkYWQ0ZjA2YjlhMDFiNzBhY2MyNDcyM2RhYWUyZDcxIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvcmVtb3ZlU21hbGxHZW9tZXRyeS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImNjMGFhM2ExZWM3OGNiYzIxMmNiZTMwOTk3ZmI3ZDM0NzcwNWQ4OWQxYmJmOTNjYjNmYmI2NTRmNjhjZDNmOTUiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9yZW1vdmVVbnR5cGVkUHJpbXMubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI4NzE4OGJjNjY2NDAyNDdmNTc1NmEyZjEyMzZiOTBkNjI3OGM4ZWRlM2FlYzI2YzQwNmQzNjAzNTQ2NGM2YTI3IiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvcmVtb3ZlVW51c2VkVVZzLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiOTZkNTM0ZmVhZDE1NWM5NGM3NzNkNTM2YzIyYjBjZmRhNWEyZjUzNjRlYzYzNzRiNjJkNmE2NzY0YmNhODE1NCIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL3J0eE1lc2hDb3VudC5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjUyNjg3Y2Q4NWIwYjAwZTc4Y2IzMDQ0NzcwMmIyMGRmODhlZWMxMDVmZDdhN2I4NzdiZWM5NzE5OTBjMGQ4YzciLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9zY3JpcHRzL29wZXJhdGlvbi1jdXJhdGlvbi5zY2hlbWEuanNvbiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjJjOTdiNGNlMDkyMDYzNTk1MmZiYTNiYTRmMmMzZWE5ZmJjZTJhNmRlNTkxNmNmOGJjMjE1YjNkNTU4ZmIxNDUiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9zY3JpcHRzL29wZXJhdGlvbi1tYW5pZmVzdC5zY2hlbWEuanNvbiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjI0MDZiZWNkOWYxYzkzMjljYTlkNGJkZWQzOWQwNjYyODQ3N2IzZDFkNmRhOTM2MDA3YzNkMmE5ZDI5NzNjMWYiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9zaHJpbmt3cmFwLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNmI4YzUxNTc3MzYxMDg5NGE0MGJhYjQ3Yjc1MDI5N2FiYTk3ODFmNGQ0M2ZmODYxNmE1YTZiMTkxYWMxN2IyYyIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcGVyYXRpb25zL3NwYXJzZU1lc2hlcy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjUyNWM3MGQ1MDA0ZjA1MDFhOWU1MDc5ZWY4NzBlOWMzNWQ0MjdlZGM1Njg4YWE2Njc2NWIyMDQ2MTU2YWEzOTIiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9zcGxpdE1lc2hlcy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjI3YmEyZjU3NTFhMDRhM2JiY2I1ZTJhMDBlYmNmNzE4ZjE5ZTJlMDA5YWM3YTFjMjhjNjM2NGZhNWQ5YzJhNTAiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3BlcmF0aW9ucy9zdWJkaXZpZGVNZXNoZXMubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI1MmM4NGE4MjZkNmVlMjcyMDg1OTY3NTQ5NWZjOGUyMzBhNjM3ZmRiN2NkODBlZjk0MGRmZjIyNTc0N2EyZDkzIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvdHJpYW5ndWxhdGVNZXNoZXMubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJlZDQ1YjVlMDhkYTNlYjk2YjBiMjg3YmMyODJhODhmNWFmODhiMDY2MmY2ZDY1ZTRhODYxNTdhNzM5YjgyMDlhIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wZXJhdGlvbnMvdXRpbGl0eUZ1bmN0aW9uLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNzIxNDIwNjkwNzA3OTQ1MDQyMDM0ZWFjNjc1OGQyZmQyN2JlZDNmOGJmMjdmYzNlY2MyMWRlZjY0MTNmNzg4YSIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcHRpbWl6YXRpb24tcmVwb3J0L1JFQURNRS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjJhNGExYmQ5YjliODg5ZGJmYjYzODRmYjkyZmZjZGRjMGRjZTg5YmYxYmVlZTdkNzBiY2U4MzdmYWE4ODA4NTIiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvb3B0aW1pemF0aW9uLXJlcG9ydC9yZWZlcmVuY2VzL29wdGltaXphdGlvbi1yZXBvcnQtdGVtcGxhdGUubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIyYmQ5ZTQ0MzAzMDIzMDg0ODY0MGEzZmIzODVjM2MwZGEyNjZmYWMzZGYxYjA2Zjc5NGJiZWI0ZTA3MTI0YTkwIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL29wdGltaXphdGlvbi1yZXBvcnQvc2NyaXB0cy9vcHRpbWl6YXRpb24tcmVwb3J0LnNjaGVtYS5qc29uIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZWM5ZDdlYjBlYjQ0ZjMxNzNiZWY1MGFlZTdhY2I4YTA2MjVhYWYxNzcwOGUwMjJmYmE5ZTIwZGVlNTE2OWQxZSIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vcHRpbWl6YXRpb24tcmVwb3J0L3NjcmlwdHMvdmFsaWRhdGVfcmVwb3J0LnB5IgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMWZjYmIyOWY5MGVkMGUzMjM5OWIxOTdmZTk4M2U1Zjk5OTY3ZTcwNWQ3NTVlZWVjN2Q3NjMwM2FmOGE4ZWNmYyIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9vdXRwdXQtd29ya3NwYWNlLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNzMwYWE4ODYwODIyOGE4MTlkZmIxNjFiNWRkMzk0MzEwYTBmZmNhNmQ3M2NlNzVlYmU2ZDNmM2JhNzc5NTk2MCIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9wcm9maWxlLXN0YWdlL1JFQURNRS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjQ3YTI2NDcyZDEyYTQ0N2M3M2QxYjEwNGMwNjllZTU0ZTRiMzQyMWUxZjQ3MDcwZWJhMGU0OGU0ZjY0NDVmMjMiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvcmVwb3J0LXRlbXBsYXRlcy9SRUFETUUubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIzYTcyNzI1ZTViMDFiY2Y4YzNhOTY5MGI3NWIyYzdhZjcyMjZhZmIzYTg5YmM0ZWUwMjE2ODZiYTZjY2Q1NmViIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3JlcG9ydC10ZW1wbGF0ZXMvb3B0aW1pemF0aW9uLXJlcG9ydC5kZXNpZ24tZml4dHVyZS5qc29uIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNDMxMmY4YWM5OWVjZDlhODY3OGQ5NTI5MTFkMmI4NzU4NTJlMDMwNjQyMzI1MmY0N2U3ZTZlZGQwYTUyMDcwOCIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9yZXBvcnQtdGVtcGxhdGVzL29wdGltaXphdGlvbi1yZXBvcnQuZGVzaWduLWZpeHR1cmUubWFuaWZlc3QuanNvbiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjJlNGVhZGJjMTUxM2U0NTJmMWQ0NWU1ZmNkZjJjMDJmY2Q1ZGNkNTUxMzJhZDQ5Mjc5YTVhODk5YTI1ZTdiOTUiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvcmVwb3J0LXRlbXBsYXRlcy9vcHRpbWl6YXRpb24tcmVwb3J0Lmh0bWwudGVtcGxhdGUiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI3ZmEyMzI1MjUzOTQwNmViNThmZjQ4MzFhZDc1ODIzNzY5ZTY3YzZhZGQ0YjJjM2QyYmU1NDIxMTEzZTc5YmE3IiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3JlcG9ydC10ZW1wbGF0ZXMvb3B0aW1pemF0aW9uLXJlcG9ydC5tZC50ZW1wbGF0ZSIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImU5Y2MwNWY1YTFmYzY2ZTIxNDU2YTQ3MTA1NTllMDU2YWIyODNjZDZlMTgxZTRlZDk2OTBjNjM5MDgzZTE3NzIiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvcmVwb3J0LXRlbXBsYXRlcy9vcHRpbWl6YXRpb24tcmVwb3J0LnN0cnVjdHVyYWwtb25seS1maXh0dXJlLmpzb24iCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJiMTNlZmM0YjEyMGUwOWVmNThmMjFhNTc2YjQ5ZTA0MzAzOTM5MGVlNzliZTNhODFhN2RkODQ1MTE0NjBjMDk0IiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3JlcG9ydC10ZW1wbGF0ZXMvcmVuZGVyX3ByZXZpZXcucHkiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI0MmNhNTkzNDYxY2NiZGQ5Y2RiZTc2ODRhZjRkZWVhNTMwNThhYzVjMTg5YmU4OTNhMDM4MzM4NjAzMjBjYjg4IiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3J1bnRpbWUtYXJ0aWZhY3QtdG9rZW4tYnVkZ2V0Lm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiOTBkYjkxNWZlYmZlYzE0ZjNjMWZlNmM3Y2U5Y2QyNzhjNTRmN2VhZTc3YjcwMDFmMDRmYTYzMWNjM2NkMTdiZCIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zZXR1cC11c2QtcGVyZm9ybWFuY2UtdHVuaW5nL1JFQURNRS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjlkNzAxZjVjODVlMTgwNzlhNzc3MmI1N2M4ZTVkOWI0Yzk2NWNlYWZkMDQzYzU4ZjI4MDQ0YTA4MGVjYTJhYjMiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvc2V0dXAtdXNkLXBlcmZvcm1hbmNlLXR1bmluZy9yZWZlcmVuY2VzL2luc3RhbGwtYXNzZXQtdmFsaWRhdG9yLXN0YW5kYWxvbmUvUkVBRE1FLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMzU3NDQ4MmIyYTUwMzc2YzE0ZTY0YmUzNTkzNGMzMmNhZWU5M2VmNDJhY2IzMTBmYjgyNWFmMDFmZmVlOThkNyIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zZXR1cC11c2QtcGVyZm9ybWFuY2UtdHVuaW5nL3JlZmVyZW5jZXMvaW5zdGFsbC1raXQvUkVBRE1FLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNTZjZGM0Njg0ZTNkZTYwODhkNmE2MjQ4ZmFiNDIwZGMxMWRkNTY3ZmM2OWJmNGZmZmU2ODdjZDdiZTllNGUwOCIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zZXR1cC11c2QtcGVyZm9ybWFuY2UtdHVuaW5nL3JlZmVyZW5jZXMvaW5zdGFsbC1zby1zdGFuZGFsb25lL1JFQURNRS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjQyYzcwOWFkMzNlMDdkOGU2NzA3ZDczYjZmNDI0YThmNmIwZTMzYmJmMDE0NDM2NmJhMDE4NDE3MDQzNWUxMWMiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvc2V0dXAtdXNkLXBlcmZvcm1hbmNlLXR1bmluZy9yZWZlcmVuY2VzL2luc3RhbGwtc28tdmlhLWtpdC9SRUFETUUubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJmMGM4MjBiNDgzZDVmMGJhNTQzM2Y2NDNhYmI5OWY4NDI0ZTM4MGMxMGI1NzdlMjQ3ZmFhNjA1Y2QyZmUxZjIxIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3NldHVwLXVzZC1wZXJmb3JtYW5jZS10dW5pbmcvcmVmZXJlbmNlcy9raXQtZGlzY292ZXJ5Lm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMjEyMDk5NDVhMmFhNDFjZGFkZjdkZGVkNGRjOWZlOTdjZGFiZjJkZTljOGViMzI0MjYwMjNhODI0ODZlMjU5YyIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zZXR1cC11c2QtcGVyZm9ybWFuY2UtdHVuaW5nL3JlZmVyZW5jZXMvcnVudGltZS1jb250ZXh0LWhlYWRlci5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjY3NjM1MTVkZjZkOTAxN2JkYWFlNjlmMzkyZWE4ZTMzYjRjODAxZDljYjkwNzc3YjIzNDExMGUyZWEwYmNkMTAiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvc2V0dXAtdXNkLXBlcmZvcm1hbmNlLXR1bmluZy9yZWZlcmVuY2VzL3J1bnRpbWUtcHJvYmUubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIxMmJkNDZkZDNiNjJhMmExNDUwYzcwZDUxMzQ4ZWYyOTQ0NTFhYWFjMzU5YjBhNWFmZDA5OTZjY2QyZGRmNjY5IiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3NldHVwLXVzZC1wZXJmb3JtYW5jZS10dW5pbmcvcmVmZXJlbmNlcy9zdGFuZGFsb25lLXJ1bnRpbWUubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJlODkwMjVhZmQ3OWUwNzA2NGRmODRmM2MxN2U4MDg3MTA2Nzc2OTQzNDM1NTA5NDE0MGEzOTI2MGRkNjRjYTQ4IiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3NldHVwLXVzZC1wZXJmb3JtYW5jZS10dW5pbmcvc2NyaXB0cy9wcm9iZS1zbmFwc2hvdC5zY2hlbWEuanNvbiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjI1YjA1ZjYwYzUwYjhlMzhjNDczM2FiODFjZTk2MGZiMWNmZjJjNDhjMmQ2NDYzMTAyZDdmNDU1ZGM1Yjc1MWYiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvc2V0dXAtdXNkLXBlcmZvcm1hbmNlLXR1bmluZy9zY3JpcHRzL3NldHVwLXByZWZsaWdodC5zY2hlbWEuanNvbiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjA0MTQ5Mzg1OWU4OGE0ZWFjNmRjZWE4ODdiMmVlOTNiYjFlMzA1MTA5NWJlZTRiNWYyYjNkMzgyMDkwMjMyN2EiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvc2tpbGwtbWFwLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMTc0YjllNWY2MTIwZDBkOWI2MDc0NDM4MmExYjUxMTQ0OWEzODkyZmJkNTg1OTBhYzAxYWNjMWEzNDgzM2IzOSIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zby1ydW4tb3BlcmF0aW9ucy9SRUFETUUubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI0ZmViZmJiNTNiYTcwNTYzY2VmODY3YzY4NDNjYzMxOGM5NmJhZWQ0M2ZmNmI0YjVhNzgyZWNiZTdlZDA1NTU4IiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3NvLXJ1bi1vcGVyYXRpb25zL3JlZmVyZW5jZXMvYmF0Y2gtbW9kZS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImEyYjUwZmNiM2QxYTE2MTg2NzA4YjQ0NGMwM2VmODkxYjM1NjdmMjhlZDMxZWYyY2Q2NGJlYTFlNGI0YzhhZTIiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvc28tcnVuLW9wZXJhdGlvbnMvcmVmZXJlbmNlcy9jb25maWctZnJvbS1ldmlkZW5jZS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjFmNWQ1NGE4OWFjNDliMDQ3NjdmNDUzZWUzMTM2M2QzNzFiYzMwYTVlNjIxNDY3MGE3ZWJiMGFjM2FmZTg3YzMiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvc28tcnVuLW9wZXJhdGlvbnMvcmVmZXJlbmNlcy9pbnZvY2F0aW9uLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZTNkNWQwMGM0YTNlYmMxNjk3NTI3MjJiMDZlNzMxNjUyOWY5NTQ3NjBhNmMxZWQ3MGNlMzc5ZGZlOWIyMzcwNSIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zby1ydW4tb3BlcmF0aW9ucy9yZWZlcmVuY2VzL29wZXJhdGlvbi1zYWZldHkubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJmNzA0NDI2NjZlMWY2NGMxMWVjMTY0Y2Q2MjZiZTkyZDg2NWFkODZlMTg3YWQ5NDYyODAyNzExZmM4NzdmN2FkIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3NvLXJ1bi1vcGVyYXRpb25zL3JlZmVyZW5jZXMvcGlwZWxpbmVzLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZGI0YjJkM2JhNjI3Nzk4MGMyNmY1ZWIwMTI4YTVhNmRhODRkMGMxMDdkMGQ1NDljZjkyZTNjNTQ2Y2VhOGEwYiIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zby1ydW4tb3BlcmF0aW9ucy9yZWZlcmVuY2VzL3NvLWNyZWF0ZS1wcm94eS9SRUFETUUubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJlYjJkMzA0ZWRmZjJiZWU3MTMxYjIyY2QwYWM3NzczN2UzNTdkMTU2OWZkM2ExZDk5ZjJkMmY2NGE5ZGNkMGRlIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3NvLXJ1bi1vcGVyYXRpb25zL3JlZmVyZW5jZXMvc28tY3JlYXRlLXByb3h5L3JlZmVyZW5jZXMvYm91bmRpbmctYm94LXByb3h5LW1vZGVzLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNTdlZThkZTk0NTMyYTc4OTkyZWI1MTc5MDY3ZTIzNDE5NjNhMzZiOWIzNWM2ZjFlNGYxYjljYjAzY2U0ZjZmZiIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy9zby1ydW4tb3BlcmF0aW9ucy9yZWZlcmVuY2VzL3NvLWNyZWF0ZS1wcm94eS9yZWZlcmVuY2VzL2RlY2ltYXRlLXN0ZXAtcmVjaXBlcy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjM3OTc3NjgyNTVmOTJiNTU1MDljMDJhMDMxNzhhYWJlYTQ4ZmFmN2Q1Zjc5YjNmMGEzZDk5MWY0ODMwNGY4MjIiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvc28tcnVuLW9wZXJhdGlvbnMvcmVmZXJlbmNlcy9zby1jcmVhdGUtcHJveHkvcmVmZXJlbmNlcy9kZWNpbWF0aW9uLXR1bmluZy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjlkMzhjYmNmMmMxNWZlOGYzZDI5ZTIzMjAyNjEzM2I0ZjkwNGVjOWE3Y2M0MjkxOGIyNWE5ZTNkZmU4YTY5NzciLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvc28tcnVuLW9wZXJhdGlvbnMvcmVmZXJlbmNlcy9zby1jcmVhdGUtcHJveHkvcmVmZXJlbmNlcy9wcm94eS1jb25maWctcmVjaXBlcy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjk2MGM0YjA4NWUxZDg3MzcxYzA2ODllNWNmNDc3MmEyODM4MTNmYWZkZTRkZWE4YzY2MzJmOTFmNWVjNTAzNzgiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvc28tcnVuLW9wZXJhdGlvbnMvcmVmZXJlbmNlcy91bml0cy1hbmQtdG9sZXJhbmNlcy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjc4OWFhOGUwYWQzOTY3NjM1NzVhZTk2MTcwYThhZGY5Yjg2YzBlOWE1ZTE5YjhlYjhmNDhhMWZlMmUxNTZlMjAiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXBzdHJlYW1zL1JFQURNRS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjkxYWRkYWFiODU5MzA4OTY3NDE0OTBjMzBmNzljMmQ0YWQwZTY5NTBlNDNkY2YzMGQ3ZDBmMDAyY2QwNDY2NmQiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXBzdHJlYW1zL3VzZC1vcHRpbWl6ZS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjZmYWQ2NzBkZjI3MWIxN2UxMjlmN2U2MzVmOTQ4ZDQ1MTYxNTQ4N2Q1NGFmYTkyNDFkMzRlZDVjNWExZjQ0YjMiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXN0cnVjdHVyZS1hc3Nlc3NtZW50L1JFQURNRS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjNkYWMxMTM4ZWViOTk3Y2RlOTU1MGVlNWY5MjM3ZWY0ZTFmODhjMGIwZWQxN2YwMTY2YzhlZDhmNGJjZDVmNTMiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXN0cnVjdHVyZS1hc3Nlc3NtZW50L3JlZmVyZW5jZXMvYXBwbHktcmVzdHJ1Y3R1cmUvUkVBRE1FLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNzUzMDE5MGM5MDc0YzQwMjkwOGE2YzhhMzAyNzRlODg0YTU0MzdkNmM4MmJiMjc1MDhiZmM0YThkMGUwNzI1MiIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvcmVmZXJlbmNlcy9hcHBseS1yZXN0cnVjdHVyZS9yZWZlcmVuY2VzL2hpZXJhcmNoeS1kZWR1cGUtcmV3cml0ZS10b29sLXNwZWMubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJkNDJmNTBlMDUwMjQwOTJiN2MzYzMzM2QzNmNhYjk4YzFkMDUwYTc3MDE1YmMzNzQ5YThkMTQyNTA3YzliMGExIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC1zdHJ1Y3R1cmUtYXNzZXNzbWVudC9yZWZlcmVuY2VzL2FwcGx5LXJlc3RydWN0dXJlL3JlZmVyZW5jZXMvcmVmLXJlbWFwLW1vZGUubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICIzMDBhOGE4NzVmZDZmY2Q5ZDFiNzljNDAyNjRiNzdiZGQ3YTMzYjkzZWIxMWJlZWI1Mzk3ZGFmOTM1ZGYwNjI3IiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC1zdHJ1Y3R1cmUtYXNzZXNzbWVudC9yZWZlcmVuY2VzL2FwcGx5LXJlc3RydWN0dXJlL3JlZmVyZW5jZXMvcmVzdHJ1Y3R1cmUtbW9kZS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImI5NjVjMDQxZTg3NTViNWViMGNiMjAxODA2ZTk4MGFlMmIyMzE3ZWE1MjExNWU1YmMyMGI5MTc3OGEwNDc3YzYiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXN0cnVjdHVyZS1hc3Nlc3NtZW50L3JlZmVyZW5jZXMvYXBwbHktcmVzdHJ1Y3R1cmUvc2NyaXB0cy9hcHBseS1yZXN0cnVjdHVyZS1tYW5pZmVzdC5zY2hlbWEuanNvbiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjEyMDYxYTgxNmRiMjE3ZTA2YWI4ZDBlZTViMjVhZGE5OWJmMTQxYmNjYWFlMTE2YmQzZDJjNjU0ZjFkN2ZlNDYiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXN0cnVjdHVyZS1hc3Nlc3NtZW50L3JlZmVyZW5jZXMvYXNzZXQtc3RydWN0dXJlLXByaW5jaXBsZXMubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJjN2I2YTllMDdhN2M1MjJkNTg0YWZlMzNlYzk3YTZkN2RjNmVlZTM1MzJjMjY4YThhZWZhMDU2YzBiZDMzOTNmIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC1zdHJ1Y3R1cmUtYXNzZXNzbWVudC9yZWZlcmVuY2VzL2NvbXBvc2l0aW9uLWF1ZGl0Lm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMjYyMWE2OWEzZWJjZWE0ZWVhZjlkYjdjMzJlZjA0Y2MzYzYyNWUwZDAwNTcwNGEyNTQ3YTUyNGNkNmI5NTRhNSIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvcmVmZXJlbmNlcy9mYWN0b3J5LWxldmVsLXN0cnVjdHVyaW5nLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNWIzOTNlZjAyMTNkZWFiZjNhNTdkZmM2MjdjZDkwMTNlNWEyOGY2YmIxMTMyOGQxMTBmYTc3MzY4Y2IxOWY2MiIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvcmVmZXJlbmNlcy9pbnN0YW5jaW5nLXJlYWRpbmVzcy9SRUFETUUubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI1YzJjNWMzNWQ3ZWI5YjYyMzQxMjZjZTQzMGQwOTIwZmNmODI3MDkxNGM3OTQ5MTY5NWE3N2UzZGQzMzYwYmNkIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC1zdHJ1Y3R1cmUtYXNzZXNzbWVudC9yZWZlcmVuY2VzL2luc3RhbmNpbmctcmVhZGluZXNzL3JlZmVyZW5jZXMvaW5zdGFuY2luZy1ndWlkZS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjc1NTM4NTllZTE1YzU3ZDk0OTlkZDZjZWNhMzE2NzAyZTU0MDkwM2VjMGIzM2EzYTU1MTY3YWI3ZGViMzYwMDkiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXN0cnVjdHVyZS1hc3Nlc3NtZW50L3JlZmVyZW5jZXMvaW5zdGFuY2luZy1yZWFkaW5lc3MvcmVmZXJlbmNlcy9pbnN0YW5jaW5nLXRyYWRlb2Zmcy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjgzYWM0ZWNjY2YxYjVjZDc3ZDhkZDljMDVkMWRiMjYyNTE3NDE1ZWFlNDQ5MzMwNDNjY2MzYzJmNjdlYTcxZDAiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXN0cnVjdHVyZS1hc3Nlc3NtZW50L3JlZmVyZW5jZXMvbGF5ZXItaGVhbHRoLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiM2JjOWE0NDg2YzFkYzU3NGZmNWYzNmMyYTRiYjQyZTk1OTJiNGUyNmNlNTk3ZGE0Nzg1Mjc2MjU4MzBiYTI5ZCIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvcmVmZXJlbmNlcy9vcHRpbWl6YXRpb24tdHJhZGVvZmZzLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiNjEyYTZiYWIxM2EwYjI4ZDliM2ZkNTg3OWM3MzExMDY3ZjUzMjFjNzk5MTY2ZjgwNTFjYjZjZDY1ZjMyYWMzZSIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvcmVmZXJlbmNlcy9yZXN0cnVjdHVyZS1kZWNpc2lvbi9SRUFETUUubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICI2NGRmMmE5NjAwOGFjYzJiODRhOTgwY2U1Y2ZkY2Q3OWY2MDJjNTIxYTBmYWVhZGYxYmMzNWZkMWFhZjhiNDRkIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC1zdHJ1Y3R1cmUtYXNzZXNzbWVudC9yZWZlcmVuY2VzL3VzZC1lZGl0LXRhcmdldC1wbGFubmVyL1JFQURNRS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjVjMDFlM2VhYTUzNmNmMTFiMzA3MzQwOTE2YTk5ZDJiNTUzMzYzNTk0MGJiMTI0NjU3Y2Y2OGZlNmU4MmZhNmQiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXN0cnVjdHVyZS1hc3Nlc3NtZW50L3JlZmVyZW5jZXMvdXNkLWVkaXQtdGFyZ2V0LXBsYW5uZXIvcmVmZXJlbmNlcy9vdXRwdXQtc2F2aW5nLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZWIyMjExYjMyMzQxOGFmMzdiZThlYTIwNzYzZDliYTRiN2RlNjE1NjJmODM5NWFjNjgzZjk4ZGUzMTg2MDU5YyIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvcmVmZXJlbmNlcy91c2QtZWRpdC10YXJnZXQtcGxhbm5lci9yZWZlcmVuY2VzL3ZhcmlhbnRzLXBheWxvYWRzLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMzE4NGNiMzcwZWI3MDQzZTYxZTQ2MzdmZjRmNzBiZDg5ZDJhMjI4YTYzNWUzNmFkOGYzZDcyOWZkODRmODZkNSIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvcmVmZXJlbmNlcy91c2QtaGllcmFyY2h5LWRlZHVwZS1jYW5kaWRhdGVzL1JFQURNRS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImYxN2Y1NGE1MDg1MzM3ZDcxNTVlYWYyOGUwNzVjNzliNDgxZmYyYTRjZjQ0YTM5MGI4Y2RjNDRjOTM5YzE2ZjQiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXN0cnVjdHVyZS1hc3Nlc3NtZW50L3JlZmVyZW5jZXMvdXNkLWhpZXJhcmNoeS1kZWR1cGUtY2FuZGlkYXRlcy9yZWZlcmVuY2VzL2luc3RhbmNlLWNhbmRpZGF0ZS1maW5kZXItc3BlYy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjI2NTFmZjQ2ZDhmYmZjZWVhOGFhMmE3OTA4OTc5NjM2ODBkZGE5MjkzOWQzZjY0ZTEwNGFjODM1ZGNkMDZkMWIiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXN0cnVjdHVyZS1hc3Nlc3NtZW50L3NjcmlwdHMvYXVkaXQtcmVwb3J0LnNjaGVtYS5qc29uIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMWM4Zjg5YzUwMWYyNzE0NGIxOGM0ZjZhZjNiYTMxY2Y1Yzc3OWExNmNhMjJiOTU0OTJlNTUyNjI0YzNhZGZjNSIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2Qtc3RydWN0dXJlLWFzc2Vzc21lbnQvc2NyaXB0cy9vcHRpbWl6YXRpb24tcGxhbi5zY2hlbWEuanNvbiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImQzZWJlZjQxMTE1MmRjYWFlYzFiN2VlNjQxOWMyZDQyOGFhY2FlNmI1ZTkzOTE1M2M1YTI4ZmQwYzJlMTNlNzYiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXN0cnVjdHVyZS1hc3Nlc3NtZW50L3NjcmlwdHMvdXNkLXN0cnVjdHVyZS1hc3Nlc3NtZW50LXJlcG9ydC5zY2hlbWEuanNvbiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjY4MmEzZjhlYjQ5ZTI1MTI4MDU0NTYzZjA1YjU3NTVmMWEwNTlhYmIwM2I1YjVkYmNlMmRhOWJmZmYzZmZlOTIiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXZhbGlkYXRpb24tcnVubmVyL1JFQURNRS5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImJkNzBiOTRkMDY1NTQ1ZDA0ODFjZGMwZGMzYmVhZTlhMTA2YTliYmMxYzJkNTQzZWUyMjU2MTBiMzYxZGFiMjciLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXZhbGlkYXRpb24tcnVubmVyL3JlZmVyZW5jZXMvc28taW50ZXJwcmV0LXZhbGlkYXRvcnMvUkVBRE1FLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZTk0Yzc5MTIyYjY3OTZiMWExODNmMGQ2NDU1MTM0MWMyZDAwYmZkZDAxMWQ3NmJkZTNhMDc5N2RiNDIwYjVhMCIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2QtdmFsaWRhdGlvbi1ydW5uZXIvcmVmZXJlbmNlcy9zby1pbnRlcnByZXQtdmFsaWRhdG9ycy9yZWZlcmVuY2VzL2ZvbGxvdy11cC1xdWVyaWVzLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiYjMwMzRjNDJjNWVlNjRjYmZmN2I3YmI4ZjRiMzk5ZTQ1ODQ3NDVkNDEyYTc3ZGFlMWY0OGIyMDg1MjhlNTdmMCIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2QtdmFsaWRhdGlvbi1ydW5uZXIvcmVmZXJlbmNlcy9zby1pbnRlcnByZXQtdmFsaWRhdG9ycy9yZWZlcmVuY2VzL3J1bGUtcmVmZXJlbmNlLm1kIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiM2IzM2YwYTY4YTE3ZDgyNjk5NGQ3YjczZDc5NzgxODA5MzRlYTc1NmQyYWNjZjc3NzBiZTFiNmNlOWU4MmVjNiIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2QtdmFsaWRhdGlvbi1ydW5uZXIvcmVmZXJlbmNlcy9zby1ydW4tdmFsaWRhdG9ycy9SRUFETUUubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJmNjE0NDIyMTU2YjdiMDQ0Njc1YmM4YzUwZTBmNDNiMTY3NDE3NmRjMzA2Mzg5YTZmMGZmODc2YzE0MjY5YmI2IiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC12YWxpZGF0aW9uLXJ1bm5lci9yZWZlcmVuY2VzL3NvLXJ1bi12YWxpZGF0b3JzL3JlZmVyZW5jZXMvaW5mcmFzdHJ1Y3R1cmUubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJlNTNlNjg1NzY1NTYwMGRjNGFjMTg1MjE5M2E2MmVmYmYzMzkxMDNmNmRjNWRhNTZmNmY5ZWE3OWViNDRmYmVjIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC12YWxpZGF0aW9uLXJ1bm5lci9yZWZlcmVuY2VzL3ZhbGlkYXRlLXVzZC1hc3NldC12YWxpZGF0b3IubWQiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJmODhhNGQzZWRhMjk5ZDg2NzAzMDgwMGY3NGIxZDgwNmZmMmI4ZDJkMDM1NTNiNzJiYTIzMTYwNWQzY2ViMDViIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC12YWxpZGF0aW9uLXJ1bm5lci9yZWZlcmVuY2VzL3ZhbGlkYXRvci1jb25jZXB0cy5qc29uIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiMmZkYWE0M2YyODczZGU2MTM2NTQwZGI5ZTUwNmM3ZWIyNmI0ZWYwNWZkMDY5NmY5YjBkMjFmNzJkNzY2MzMxMCIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy91c2QtdmFsaWRhdGlvbi1ydW5uZXIvc2NyaXB0cy91c2RfdmFsaWRhdGlvbl9leGVjdXRvci5weSIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjljOTlhZjI3OWUyZDMwZjg3MGZmMjk1ZGQ3N2ZkYzBiNjA1NGFmMzAxMmEyZDI3ZWRkOGZkZTYyZjA0M2E0MDgiLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXZhbGlkYXRpb24tcnVubmVyL3NjcmlwdHMvdmFsaWRhdGlvbi1yZXBvcnQuc2NoZW1hLmpzb24iCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWxnb3JpdGhtIjogInNoYTI1NiIsCiAgICAgICAgImRpZ2VzdCI6ICJmNWNiNGFlN2Q5ZDc5NWYzYjgwYWFiMmJkMWNiYjZjMzc3NzNiYzJmMTVkZTYzNmFmMTczYmQxOGI0NGFjYTAxIiwKICAgICAgICAibmFtZSI6ICJyZWZlcmVuY2VzL3VzZC12YWxpZGF0aW9uLXJ1bm5lci9zY3JpcHRzL3ZhbGlkYXRpb24tc2NvcGUtbm90ZS5zY2hlbWEuanNvbiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogImNmNTI1ZWQ2MzBmZmMzYTAyYzgwOTBiZDQ3NTVkNGQzNDBmYTEyN2JlMDMyNWIyYmY3OTkwMGE2YTc4MWRjYTciLAogICAgICAgICJuYW1lIjogInJlZmVyZW5jZXMvdXNkLXZhbGlkYXRpb24tcnVubmVyL3NjcmlwdHMvdmFsaWRhdG9yLWNvbmNlcHRzLnNjaGVtYS5qc29uIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFsZ29yaXRobSI6ICJzaGEyNTYiLAogICAgICAgICJkaWdlc3QiOiAiZTcyOTYxM2JhYzRjZmRkZmYyY2Y3Njc2NDAxM2Y2ZGYwOGQyOGY2ZjJhYWVjMzM5OTU1NTRkNTg1MTllNTc1ZSIsCiAgICAgICAgIm5hbWUiOiAicmVmZXJlbmNlcy93b3JrZmxvdy5tZCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbGdvcml0aG0iOiAic2hhMjU2IiwKICAgICAgICAiZGlnZXN0IjogIjc3NWVmOTMyMmQzMzBiMmRlZjAzZTNjNzRjOGRjMzNjZmRhNDExY2Q5NDk0YmEzYmY4NDUxOGQ4MWQ3NWU0ODQiLAogICAgICAgICJuYW1lIjogInNraWxsLWNhcmQubWQiCiAgICAgIH0KICAgIF0sCiAgICAic2VyaWFsaXphdGlvbiI6IHsKICAgICAgImlnbm9yZV9wYXRocyI6IFsKICAgICAgICAiLmdpdGh1YiIsCiAgICAgICAgIi5naXRpZ25vcmUiLAogICAgICAgICIuZ2l0IiwKICAgICAgICAiLmdpdGF0dHJpYnV0ZXMiCiAgICAgIF0sCiAgICAgICJtZXRob2QiOiAiZmlsZXMiLAogICAgICAiaGFzaF90eXBlIjogInNoYTI1NiIsCiAgICAgICJhbGxvd19zeW1saW5rcyI6IGZhbHNlCiAgICB9CiAgfQp9","payloadType":"application/vnd.in-toto+json","signatures":[{"sig":"MGUCMQD4bjufbPKiPuqBBuobdiE76lbwM7nBOvqJa0ikxsXnFYD98+sHlSiXRPDETQwP0AsCMDJuG2+rsHKufVj2CZr6vrS/BpfyUCsOoPn8ua++wEykO1y1YtYqGge8hKCH2MwedA==","keyid":""}]}} \ No newline at end of file