Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
c96de62
fix(security): remediate OpenClaw transitive dependencies
jyaunches Jul 21, 2026
b3fb226
fix(security): complete OpenClaw transitive remediation
jyaunches Jul 21, 2026
a98e9b9
test(security): keep remediation fixture linear
jyaunches Jul 21, 2026
223aea5
test(security): cover remediated plugin archives
jyaunches Jul 21, 2026
c9aa63c
fix(security): preserve OpenClaw global dependencies
jyaunches Jul 21, 2026
f85e877
test(security): cover installed dependency smoke check
jyaunches Jul 21, 2026
6752f3e
fix(onboard): stabilize authoritative rebuilds
cjagwani Jul 21, 2026
e56fab9
test(e2e): budget authoritative base rebuilds
jyaunches Jul 21, 2026
4490f96
test(e2e): provision swap for Hermes rebuilds
jyaunches Jul 21, 2026
1bdd32c
test(e2e): repair legacy Hermes fixture permissions
jyaunches Jul 21, 2026
6f4158e
test(e2e): run Hermes fixture commands outside Landlock
cjagwani Jul 21, 2026
fe649b0
test(e2e): simplify legacy Hermes fixture image
jyaunches Jul 21, 2026
c9396cf
test(e2e): expand Hermes rebuild swap
jyaunches Jul 21, 2026
4430f7b
test(e2e): preserve Hermes runtime state path
jyaunches Jul 21, 2026
1acc180
test(e2e): pin Hermes kanban database
jyaunches Jul 21, 2026
37929a3
fix(state): fail closed on sqlite backup errors
jyaunches Jul 21, 2026
d24912a
fix(state): isolate sqlite backup runtime
jyaunches Jul 21, 2026
33d10a7
fix(state): use system Python for SQLite snapshots
jyaunches Jul 21, 2026
b26cf68
ci(e2e): retry trusted PR gate
jyaunches Jul 21, 2026
3644f98
test(e2e): provision trusted Hermes rebuild swap
jyaunches Jul 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/base-image.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ on:
- "agents/openclaw/mcporter-runtime/package-lock.json"
# Dockerfile.base validates min_openclaw_version from this file at build time.
- "nemoclaw-blueprint/blueprint.yaml"
- "scripts/lib/openclaw-npm-remediation.mts"
- "scripts/lib/reviewed-npm-archive.mts"
- "scripts/lib/sandbox-rlimits.sh"
workflow_dispatch:
Expand Down
20 changes: 20 additions & 0 deletions .github/workflows/e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2290,6 +2290,16 @@ jobs:
with:
build-cli: "false"

- name: Add swap for Hermes image rebuild
shell: bash
run: |
set -euo pipefail
sudo fallocate -l 32G /mnt/nemoclaw-hermes-rebuild.swap
sudo chmod 0600 /mnt/nemoclaw-hermes-rebuild.swap
sudo mkswap /mnt/nemoclaw-hermes-rebuild.swap
sudo swapon /mnt/nemoclaw-hermes-rebuild.swap
swapon --show

- name: Run Hermes rebuild live test
# Preserves the real
# install.sh, Docker/OpenShell, Hermes base-image rebuild, registry,
Expand Down Expand Up @@ -2365,6 +2375,16 @@ jobs:
with:
build-cli: "false"

- name: Add swap for Hermes image rebuild
shell: bash
run: |
set -euo pipefail
sudo fallocate -l 32G /mnt/nemoclaw-hermes-rebuild.swap
sudo chmod 0600 /mnt/nemoclaw-hermes-rebuild.swap
sudo mkswap /mnt/nemoclaw-hermes-rebuild.swap
sudo swapon /mnt/nemoclaw-hermes-rebuild.swap
swapon --show

- name: Run Hermes stale-base rebuild live test
# Uses NEMOCLAW_HERMES_STALE_BASE_REBUILD_E2E=1, preserving issue #3025's
# stale cached base-image regression boundary.
Expand Down
18 changes: 15 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ COPY agents/openclaw/mcporter-runtime/package-lock.json /usr/local/lib/nemoclaw/
COPY agents/openclaw/wechat-runtime/package.json /usr/local/lib/nemoclaw/wechat-runtime/package.json
COPY agents/openclaw/wechat-runtime/package-lock.json /usr/local/lib/nemoclaw/wechat-runtime/package-lock.json
COPY scripts/lib/reviewed-npm-archive.mts /scripts/lib/reviewed-npm-archive.mts
COPY scripts/lib/openclaw-npm-remediation.mts /scripts/lib/openclaw-npm-remediation.mts

# OpenShell blocks the link-local EC2 Instance Metadata Service. Keep AWS SDK
# credential chains from attempting an impossible metadata discovery path.
Expand Down Expand Up @@ -287,6 +288,8 @@ RUN set -eu; \
if [ -z "$EXPECTED_INTEGRITY" ]; then \
echo "ERROR: OpenClaw ${OPENCLAW_VERSION} has no committed npm integrity pin" >&2; exit 1; \
fi; \
OPENCLAW_RECIPE='ignore-scripts+reviewed-lifecycle-v1'; \
if [ "$OPENCLAW_VERSION" = "2026.6.10" ]; then OPENCLAW_RECIPE='ignore-scripts+reviewed-lifecycle+transitive-remediation-v1'; fi; \
MCPORTER_EXPECTED_INTEGRITY=""; \
MCPORTER_EXPECTED_TARBALL=""; \
if [ "$MCPORTER_VERSION" = "0.7.3" ]; then MCPORTER_EXPECTED_INTEGRITY="$MCPORTER_0_7_3_INTEGRITY"; MCPORTER_EXPECTED_TARBALL="$MCPORTER_0_7_3_TARBALL"; fi; \
Expand All @@ -307,7 +310,7 @@ RUN set -eu; \
"package=openclaw@${OPENCLAW_VERSION}" \
"integrity=${EXPECTED_INTEGRITY}" \
"tarball=${EXPECTED_TARBALL}" \
'recipe=ignore-scripts+reviewed-lifecycle-v1' \
"recipe=${OPENCLAW_RECIPE}" \
"mcporter-package=mcporter@${MCPORTER_VERSION}" \
"mcporter-integrity=${MCPORTER_EXPECTED_INTEGRITY}" \
"mcporter-tarball=${MCPORTER_EXPECTED_TARBALL}" \
Expand Down Expand Up @@ -337,10 +340,16 @@ RUN set -eu; \
echo "ERROR: Base image has OpenClaw $CUR_VER, which is newer than reviewed target $OPENCLAW_VERSION" >&2; exit 1; \
else \
echo "INFO: Base image OpenClaw $CUR_VER lacks exact reviewed provenance; installing $OPENCLAW_VERSION"; \
OPENCLAW_PACK_PATH="$(node --experimental-strip-types /scripts/lib/reviewed-npm-archive.mts \
OPENCLAW_SOURCE_PACK_PATH="$(node --experimental-strip-types /scripts/lib/reviewed-npm-archive.mts \
--package-spec "openclaw@${OPENCLAW_VERSION}" --integrity "$EXPECTED_INTEGRITY" \
--tarball-url "$EXPECTED_TARBALL" --label "OpenClaw ${OPENCLAW_VERSION}")"; \
OPENCLAW_PACK_DIR="$(dirname "$OPENCLAW_PACK_PATH")"; \
OPENCLAW_PACK_DIR="$(dirname "$OPENCLAW_SOURCE_PACK_PATH")"; \
OPENCLAW_PACK_PATH="$OPENCLAW_SOURCE_PACK_PATH"; \
if [ "$OPENCLAW_VERSION" = "2026.6.10" ]; then \
OPENCLAW_PACK_PATH="$(node --experimental-strip-types /scripts/lib/openclaw-npm-remediation.mts \
--archive "$OPENCLAW_SOURCE_PACK_PATH" --package-spec "openclaw@${OPENCLAW_VERSION}" \
--working-directory "$OPENCLAW_PACK_DIR")"; \
fi; \
# npm 10's atomic-move install can hit EROFS on overlayfs when the prior
# install spans image layers. Removing it first also prevents unreviewed
# files from surviving a same-version reinstall.
Expand All @@ -353,6 +362,9 @@ RUN set -eu; \
esac; \
rm -rf "$OPENCLAW_PACK_DIR"; \
fi; \
if [ "$OPENCLAW_VERSION" = "2026.6.10" ]; then \
npm ls -g --depth=1 openclaw @openclaw/fs-safe tar jszip >/dev/null; \
fi; \
if [ "$USE_REVIEWED_BASE_RUNTIME" = "1" ]; then \
echo "INFO: Reusing reviewed base mcporter $CUR_MCPORTER_VER with exact lock provenance"; \
else \
Expand Down
20 changes: 16 additions & 4 deletions Dockerfile.base
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ ARG MCPORTER_0_7_3_TARBALL=https://registry.npmjs.org/mcporter/-/mcporter-0.7.3.
COPY agents/openclaw/mcporter-runtime/package.json /usr/local/lib/nemoclaw/mcporter-runtime/package.json
COPY agents/openclaw/mcporter-runtime/package-lock.json /usr/local/lib/nemoclaw/mcporter-runtime/package-lock.json
COPY scripts/lib/reviewed-npm-archive.mts /scripts/lib/reviewed-npm-archive.mts
COPY scripts/lib/openclaw-npm-remediation.mts /scripts/lib/openclaw-npm-remediation.mts

# Keep OpenClaw's jiti-generated source cache out of /tmp so provider marker
# names do not persist in runtime snapshots or leak-scan inputs.
Expand Down Expand Up @@ -265,13 +266,21 @@ RUN --mount=type=bind,source=nemoclaw-blueprint/blueprint.yaml,target=/tmp/bluep
if [ -z "$EXPECTED_INTEGRITY" ]; then \
echo "Error: OpenClaw ${OPENCLAW_VERSION} has no committed npm integrity pin"; exit 1; \
fi; \
OPENCLAW_PACK_PATH="$(node --experimental-strip-types /scripts/lib/reviewed-npm-archive.mts \
OPENCLAW_SOURCE_PACK_PATH="$(node --experimental-strip-types /scripts/lib/reviewed-npm-archive.mts \
--package-spec "openclaw@${OPENCLAW_VERSION}" --integrity "$EXPECTED_INTEGRITY" \
--tarball-url "$EXPECTED_TARBALL" --label "OpenClaw ${OPENCLAW_VERSION}")"; \
if [ -z "$OPENCLAW_PACK_PATH" ] || [ ! -f "$OPENCLAW_PACK_PATH" ] || [ -L "$OPENCLAW_PACK_PATH" ]; then \
if [ -z "$OPENCLAW_SOURCE_PACK_PATH" ] || [ ! -f "$OPENCLAW_SOURCE_PACK_PATH" ] || [ -L "$OPENCLAW_SOURCE_PACK_PATH" ]; then \
echo "Error: reviewed OpenClaw archive path is empty or invalid"; exit 1; \
fi; \
OPENCLAW_PACK_DIR="$(dirname "$OPENCLAW_PACK_PATH")"; \
OPENCLAW_PACK_DIR="$(dirname "$OPENCLAW_SOURCE_PACK_PATH")"; \
OPENCLAW_PACK_PATH="$OPENCLAW_SOURCE_PACK_PATH"; \
OPENCLAW_RECIPE='ignore-scripts+reviewed-lifecycle-v1'; \
if [ "$OPENCLAW_VERSION" = "2026.6.10" ]; then \
OPENCLAW_PACK_PATH="$(node --experimental-strip-types /scripts/lib/openclaw-npm-remediation.mts \
--archive "$OPENCLAW_SOURCE_PACK_PATH" --package-spec "openclaw@${OPENCLAW_VERSION}" \
--working-directory "$OPENCLAW_PACK_DIR")"; \
OPENCLAW_RECIPE='ignore-scripts+reviewed-lifecycle+transitive-remediation-v1'; \
fi; \
npm install -g --ignore-scripts "$OPENCLAW_PACK_PATH" \
&& case "$OPENCLAW_VERSION" in \
2026.4.24|2026.6.10) node /usr/local/lib/node_modules/openclaw/scripts/postinstall-bundled-plugins.mjs ;; \
Expand All @@ -283,6 +292,9 @@ RUN --mount=type=bind,source=nemoclaw-blueprint/blueprint.yaml,target=/tmp/bluep
&& if [ "$OPENCLAW_INSTALLED_VERSION" != "$OPENCLAW_VERSION" ]; then \
echo "Error: Installed OpenClaw ${OPENCLAW_INSTALLED_VERSION:-unknown} does not match reviewed target ${OPENCLAW_VERSION}"; exit 1; \
fi \
&& if [ "$OPENCLAW_VERSION" = "2026.6.10" ]; then \
npm ls -g --depth=1 openclaw @openclaw/fs-safe tar jszip >/dev/null; \
fi \
&& MCPORTER_EXPECTED_INTEGRITY="" \
&& MCPORTER_EXPECTED_TARBALL="" \
&& if [ "$MCPORTER_VERSION" = "0.7.3" ]; then MCPORTER_EXPECTED_INTEGRITY="$MCPORTER_0_7_3_INTEGRITY"; MCPORTER_EXPECTED_TARBALL="$MCPORTER_0_7_3_TARBALL"; fi \
Expand Down Expand Up @@ -310,7 +322,7 @@ RUN --mount=type=bind,source=nemoclaw-blueprint/blueprint.yaml,target=/tmp/bluep
"package=openclaw@${OPENCLAW_VERSION}" \
"integrity=${EXPECTED_INTEGRITY}" \
"tarball=${EXPECTED_TARBALL}" \
'recipe=ignore-scripts+reviewed-lifecycle-v1' \
"recipe=${OPENCLAW_RECIPE}" \
"mcporter-package=mcporter@${MCPORTER_VERSION}" \
"mcporter-integrity=${MCPORTER_EXPECTED_INTEGRITY}" \
"mcporter-tarball=${MCPORTER_EXPECTED_TARBALL}" \
Expand Down
3 changes: 2 additions & 1 deletion agents/hermes/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,11 @@ COPY agents/hermes/host/managed-tool-gateway-matrix.json /opt/nemoclaw-hermes-co
COPY src/lib/tool-disclosure.ts /src/lib/tool-disclosure.ts
COPY src/lib/messaging/ /src/lib/messaging/
COPY scripts/lib/reviewed-npm-archive.mts /scripts/lib/reviewed-npm-archive.mts
COPY scripts/lib/openclaw-npm-remediation.mts /scripts/lib/openclaw-npm-remediation.mts
RUN find /opt/nemoclaw-hermes-config -type d -exec chmod 755 {} + \
&& find /opt/nemoclaw-hermes-config -type f -exec chmod 444 {} + \
&& chmod 444 /src/lib/tool-disclosure.ts \
&& chmod 444 /scripts/lib/reviewed-npm-archive.mts \
&& chmod 444 /scripts/lib/reviewed-npm-archive.mts /scripts/lib/openclaw-npm-remediation.mts \
&& chmod -R a+rX /src/lib/messaging

# Copy blueprint (shared infrastructure)
Expand Down
33 changes: 32 additions & 1 deletion ci/full-e2e-cold-path-calibration.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"$comment": "SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\nSPDX-License-Identifier: Apache-2.0\n\nFive independent workflow_dispatch full-e2e samples of current-main baseline 1a74b8348c2182fbf806726341186f008444e28e with the phase-measurement changes at 4544d07c8bfd500c3b64a74380ef5cd0e62089f5. All runs completed install, BuildKit prebuild without fallback, the silence assertion, and the expected first turn. Each baseline budget is derived independently so phase caps diagnose regressions; they are not portions that must sum to the root-start budget. The separate validation adjustment records four existing full-e2e job observations from three descendant heads after f62c278bd737f4f47be2e85436f65b270d5b4280 added the reviewed WeChat runtime graph to the supported sandbox image. The relevant image-building inputs listed in imageInputPaths were unchanged through 2adc8481ff3053a5a7be37d130cb183e222934ff; repository tests enforce that ancestry and unchanged-input boundary. All four jobs completed install, BuildKit prebuild without fallback, the silence assertion, and the expected first turn; two exceeded the prior root-start and/or sandbox-phase caps. The conclusion fields record the full-e2e job conclusions, not aggregate workflow conclusions. The adjustment raises only those two caps from the maximum observed value plus the existing headroom, rounded up to one second. This is a bounded post-change validation adjustment, not a replacement five-run single-SHA calibration. Retire it by replacing the baseline and removing validationAdjustment after five successful full-e2e samples from one SHA that contains the image change.",
"$comment": "SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\nSPDX-License-Identifier: Apache-2.0\n\nFive independent workflow_dispatch full-e2e samples of current-main baseline 1a74b8348c2182fbf806726341186f008444e28e with the phase-measurement changes at 4544d07c8bfd500c3b64a74380ef5cd0e62089f5. All runs completed install, BuildKit prebuild without fallback, the silence assertion, and the expected first turn. Each baseline budget is derived independently so phase caps diagnose regressions; they are not portions that must sum to the root-start budget. The separate validation adjustment records four existing full-e2e job observations from three descendant heads after f62c278bd737f4f47be2e85436f65b270d5b4280 added the reviewed WeChat runtime graph to the supported sandbox image. The relevant image-building inputs listed in imageInputPaths were unchanged through 2adc8481ff3053a5a7be37d130cb183e222934ff; repository tests enforce that ancestry and unchanged-input boundary. All four jobs completed install, BuildKit prebuild without fallback, the silence assertion, and the expected first turn; two exceeded the prior root-start and/or sandbox-phase caps. The conclusion fields record the full-e2e job conclusions, not aggregate workflow conclusions. The adjustment raises only those two caps from the maximum observed value plus the existing headroom, rounded up to one second. This is a bounded post-change validation adjustment, not a replacement five-run single-SHA calibration. Retire it by replacing the baseline and removing validationAdjustment after five successful full-e2e samples from one SHA that contains the image change. The authoritative-local-build adjustment records two functional same-head PR runs that emitted the required local base-build reason. Its allowance is the maximum excess over the normal root-start or sandbox-phase budget plus the larger of 5 seconds or 10 percent, rounded up to one second. It applies only when that reason is observed and does not alter published-image budgets.",
"schemaVersion": 1,
"calibratedAt": "2026-07-13",
"baselineMainSha": "1a74b8348c2182fbf806726341186f008444e28e",
Expand Down Expand Up @@ -327,6 +327,37 @@
"sandboxPhaseBudgetMs": 171000
}
},
"authoritativeLocalBaseBuildAdjustment": {
"validatedAt": "2026-07-21",
"triggerOutput": "Building OpenClaw sandbox base image locally because no compatible published base image was found.",
"adjustedMetrics": [
"rootStartToFirstTurnCompletion",
"nemoclaw.onboard.phase.sandbox"
],
"derivation": {
"statistic": "maximum-budget-excess",
"minimumHeadroomMs": 5000,
"relativeHeadroomPercent": 10,
"roundUpMs": 1000
},
"runs": [
{
"runId": 29806262836,
"runUrl": "https://github.com/NVIDIA/NemoClaw/actions/runs/29806262836",
"headSha": "6752f3e7918d588f53d5a3d3ebd37b8e7bf7aaab",
"rootStartToFirstTurnCompletionMs": 247993,
"sandboxPhaseMs": 196679
},
{
"runId": 29806391548,
"runUrl": "https://github.com/NVIDIA/NemoClaw/actions/runs/29806391548",
"headSha": "6752f3e7918d588f53d5a3d3ebd37b8e7bf7aaab",
"rootStartToFirstTurnCompletionMs": 248833,
"sandboxPhaseMs": 196916
}
],
"derivedAllowanceMs": 31000
},
"derivedBudgetsMs": {
"rootStartToFirstTurnCompletionBudgetMs": 205000,
"rootEndToFirstTurnCompletionBudgetMs": 14000,
Expand Down
3 changes: 2 additions & 1 deletion ci/onboard-performance-budget.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"$comment": "SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\nSPDX-License-Identifier: Apache-2.0\n\nInitial advisory budget for the cloud-onboard-e2e warm-system trace signal. Profiling traces from #3769 were not available in durable CI artifacts when #3776 was implemented. The latest three release tags only exposed one tag-matching trace artifact, from a failed v0.0.66 nightly run, so this cap is calibrated from the latest ten distinct successful main full-trace samples available on 2026-06-23. Those samples had total durations of 298250 ms, 296926 ms, 304190 ms, 294859 ms, 305013 ms, 316147 ms, 300843 ms, 292702 ms, 201332 ms, and 206250 ms; the cap uses p95 via linear interpolation (index 8.55 between samples 8 and 9) plus 25 percent, rounded up to the nearest 30 seconds.\n\nThe full-e2e cold-path baseline budgets are derived from the five current-main samples recorded in ci/full-e2e-cold-path-calibration.json. For each interval and phase independently, the baseline is nearest-rank p95 plus the larger of 5 seconds or 10 percent, rounded up to the nearest second. Four later functional full-e2e jobs completed install, BuildKit prebuild without fallback, and the expected first turn on their listed heads after the supported sandbox image changed; their separately recorded bounded validation adjustment raises only the root-start and sandbox-phase caps to the observed maximum plus 10 percent, rounded up to one second. The adjustment is retired after five successful samples from one head containing the image change, when that calibration replaces the baseline and the adjustment is removed. Independent phase caps are diagnostic regression gates, not additive portions of the root-start interval.",
"$comment": "SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\nSPDX-License-Identifier: Apache-2.0\n\nInitial advisory budget for the cloud-onboard-e2e warm-system trace signal. Profiling traces from #3769 were not available in durable CI artifacts when #3776 was implemented. The latest three release tags only exposed one tag-matching trace artifact, from a failed v0.0.66 nightly run, so this cap is calibrated from the latest ten distinct successful main full-trace samples available on 2026-06-23. Those samples had total durations of 298250 ms, 296926 ms, 304190 ms, 294859 ms, 305013 ms, 316147 ms, 300843 ms, 292702 ms, 201332 ms, and 206250 ms; the cap uses p95 via linear interpolation (index 8.55 between samples 8 and 9) plus 25 percent, rounded up to the nearest 30 seconds.\n\nThe full-e2e cold-path baseline budgets are derived from the five current-main samples recorded in ci/full-e2e-cold-path-calibration.json. For each interval and phase independently, the baseline is nearest-rank p95 plus the larger of 5 seconds or 10 percent, rounded up to the nearest second. Four later functional full-e2e jobs completed install, BuildKit prebuild without fallback, and the expected first turn on their listed heads after the supported sandbox image changed; their separately recorded bounded validation adjustment raises only the root-start and sandbox-phase caps to the observed maximum plus 10 percent, rounded up to one second. The adjustment is retired after five successful samples from one head containing the image change, when that calibration replaces the baseline and the adjustment is removed. Independent phase caps are diagnostic regression gates, not additive portions of the root-start interval. The authoritative local base-build allowance is derived separately from the same-head PR evidence recorded in ci/full-e2e-cold-path-calibration.json. It applies only when full-e2e observes the exact local-build reason and adjusts only the root-start and sandbox-phase caps; published-image runs retain the normal budgets.",
"schemaVersion": 1,
"mode": "advisory",
"scope": "cloud-onboard-e2e warm-system",
Expand All @@ -13,6 +13,7 @@
"minPercent": 30
},
"fullE2eColdPath": {
"authoritativeLocalBaseBuildAllowanceMs": 31000,
"rootStartToFirstTurnCompletionBudgetMs": 228000,
"rootEndToFirstTurnCompletionBudgetMs": 14000,
"phaseBudgetsMs": {
Expand Down
Loading
Loading