Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
64f7172
fix(dcode): single-source credential name limit
ericksoa Jul 8, 2026
3d990f4
test(e2e): isolate DCode sequential checks
ericksoa Jul 8, 2026
6060b09
fix(dcode): preserve observability across policy reloads
ericksoa Jul 8, 2026
f1f0e18
test(dcode): close advisor boundary follow-ups
ericksoa Jul 8, 2026
3de56cc
test(dcode): keep boundary assertions linear
ericksoa Jul 8, 2026
42e0610
test(e2e): preserve observability during cleanup
cjagwani Jul 8, 2026
e874dac
test(e2e): keep cleanup assertion linear
cjagwani Jul 8, 2026
6fa7bff
fix(e2e): align observability cleanup marker
ericksoa Jul 8, 2026
11541cb
fix(e2e): capture observability before policy reload
ericksoa Jul 8, 2026
66ab467
chore: merge main into DCode follow-up
ericksoa Jul 8, 2026
ac0492b
test(e2e): trust host observability intent
ericksoa Jul 8, 2026
2051e20
fix(dcode): reject unsafe observability marker paths
ericksoa Jul 8, 2026
739a4e0
test(e2e): prove marker cannot grant OTLP egress
ericksoa Jul 8, 2026
c207b32
chore(dcode): clarify runtime state lifetimes
ericksoa Jul 8, 2026
aa84a4f
chore: merge main into DCode follow-up
ericksoa Jul 9, 2026
0570b87
chore: merge main into DCode follow-up
ericksoa Jul 9, 2026
61199ad
Merge branch 'main' into fix/dcode-post-merge-e2e
cv Jul 9, 2026
b834dd0
merge: sync main into dcode policy reload branch
cv Jul 9, 2026
0e4e5ac
test(dcode): exercise marker replacement on Linux
cv Jul 9, 2026
cf994ae
test(dcode): keep platform fixture linear
cv Jul 9, 2026
3e2606c
Merge branch 'main' into fix/dcode-post-merge-e2e
cv Jul 11, 2026
7a9d3da
Merge branch 'main' into fix/dcode-post-merge-e2e
cv Jul 11, 2026
665bad6
Merge branch 'main' into fix/dcode-post-merge-e2e
cv Jul 11, 2026
54ec796
Merge branch 'main' into fix/dcode-post-merge-e2e
cv Jul 11, 2026
6d660b6
Merge branch 'main' into fix/dcode-post-merge-e2e
cv Jul 11, 2026
804f664
Merge branch 'main' into fix/dcode-post-merge-e2e
cv Jul 11, 2026
3ab89f3
Merge branch 'main' into fix/dcode-post-merge-e2e
cv Jul 12, 2026
24588d8
merge: sync current main
cv Jul 12, 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
7 changes: 5 additions & 2 deletions agents/langchain-deepagents-code/dcode-launcher.sh
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,12 @@ unset _NEMOCLAW_SANDBOX_RLIMITS
# entrypoint's environment, so an opted-in direct dcode exec can lose tracing.
# Source boundary: start.sh materializes only the credential-free enable bit;
# this launcher recovers it only from a regular, non-symlink marker.
# Source-fix constraint: NemoClaw cannot make OpenShell preserve entrypoint env.
# Source-fix constraint: NemoClaw cannot make OpenShell preserve entrypoint env,
# and policy-only reloads clear /tmp without re-running the entrypoint. Keep the
# reconstructable bit in the sandbox workspace so those reloads retain it.
# Regression: the proxy-launcher tests cover exact values and unsafe file types.
# Removal condition: OpenShell propagates the bit to every exec/login process.
# Removal condition: OpenShell propagates the bit to every exec/login process
# and preserves it across policy reloads or re-runs the entrypoint afterward.
# The marker is convenience state, not an authorization boundary; the
# host-selected network policy controls whether local OTLP egress exists.
unset NEMOCLAW_OBSERVABILITY
Expand Down
8 changes: 5 additions & 3 deletions agents/langchain-deepagents-code/dcode-wrapper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ readonly DEEPAGENTS_AUTH_FILE="/sandbox/.deepagents/.state/auth.json"
readonly DEEPAGENTS_CODEX_AUTH_FILE="/sandbox/.deepagents/.state/chatgpt-auth.json"
readonly MANAGED_DCODE_AUTO_APPROVAL_FILE="/usr/local/share/nemoclaw/dcode-auto-approval"
readonly MANAGED_DCODE_AUTO_APPROVAL_OWNER_UID=0
# Shared bound for canonical credential prefixes and OpenShell env identifiers.
readonly CREDENTIAL_NAME_PREFIX_MAX_LENGTH=128

managed_auto_approval_file_metadata() {
local file="$1"
Expand Down Expand Up @@ -155,8 +157,8 @@ has_context_secret_shape() {
upper="$(printf '%s' "$1" | tr '[:lower:]' '[:upper:]')"
# Keep horizontal separator whitespace bounded to mirror the canonical
# lookbehind and avoid an attacker-controlled scan over arbitrarily long runs.
[[ "$upper" =~ (^|[^A-Z0-9])([A-Z0-9]{1,128}_(KEY|TOKEN|SECRET|CREDENTIAL|PASSWORD|PASSWD|PASS)|(X[-_])?API[-_]KEY|TOKEN|SECRET|CREDENTIAL|PASSWORD|PASSWD|PASS)[\'\"]?([[:blank:]]{0,32}[=:][[:blank:]]{0,32}|[[:blank:]]{1,32})[\'\"]?[^[:space:]\'\"]{10,} ]] \
|| [[ "$1" =~ (^|[^A-Za-z0-9])([A-Za-z0-9]{1,128}(Token|Secret|Credential)|[A-Za-z0-9]{0,128}([Aa]ccess|[Rr]efresh|[Cc]lient|[Bb]earer|[Aa]uth|[Aa][Pp][Ii]|[Pp]rivate|[Ss]igning|[Ss]ession|[Bb]ot|[Aa]pp|[Rr]esolved)Key|[A-Za-z0-9]{1,128}(Password|Passwd|Pass))[\'\"]?([[:blank:]]{0,32}[=:][[:blank:]]{0,32}|[[:blank:]]{1,32})[\'\"]?[^[:space:]\'\"]{10,} ]] \
[[ "$upper" =~ (^|[^A-Z0-9])([A-Z0-9]{1,${CREDENTIAL_NAME_PREFIX_MAX_LENGTH}}_(KEY|TOKEN|SECRET|CREDENTIAL|PASSWORD|PASSWD|PASS)|(X[-_])?API[-_]KEY|TOKEN|SECRET|CREDENTIAL|PASSWORD|PASSWD|PASS)[\'\"]?([[:blank:]]{0,32}[=:][[:blank:]]{0,32}|[[:blank:]]{1,32})[\'\"]?[^[:space:]\'\"]{10,} ]] \
|| [[ "$1" =~ (^|[^A-Za-z0-9])([A-Za-z0-9]{1,${CREDENTIAL_NAME_PREFIX_MAX_LENGTH}}(Token|Secret|Credential)|[A-Za-z0-9]{0,${CREDENTIAL_NAME_PREFIX_MAX_LENGTH}}([Aa]ccess|[Rr]efresh|[Cc]lient|[Bb]earer|[Aa]uth|[Aa][Pp][Ii]|[Pp]rivate|[Ss]igning|[Ss]ession|[Bb]ot|[Aa]pp|[Rr]esolved)Key|[A-Za-z0-9]{1,${CREDENTIAL_NAME_PREFIX_MAX_LENGTH}}(Password|Passwd|Pass))[\'\"]?([[:blank:]]{0,32}[=:][[:blank:]]{0,32}|[[:blank:]]{1,32})[\'\"]?[^[:space:]\'\"]{10,} ]] \
|| [[ "$1" =~ (^|[^A-Za-z0-9])KEY[\'\"]?([[:blank:]]{0,32}[=:][[:blank:]]{0,32}|[[:blank:]]{1,32})[\'\"]?[^[:space:]\'\"]{10,} ]]
}

Expand Down Expand Up @@ -443,7 +445,7 @@ is_openshell_env_placeholder_for_name() {
[ "$name" != "OPENSHELL_TLS_KEY" ] || return 1

# Keep this identifier contract aligned with OpenShell provider env keys.
if [ -z "$name" ] || [ "${#name}" -gt 128 ]; then
if [ -z "$name" ] || [ "${#name}" -gt "$CREDENTIAL_NAME_PREFIX_MAX_LENGTH" ]; then
return 1
fi
case "$name" in
Expand Down
4 changes: 3 additions & 1 deletion agents/langchain-deepagents-code/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,8 @@ write_export_if_set() {
}

prepare_runtime_env() {
# Unlike prepare_observability_marker below, this file is intentionally
# volatile: every stateful start rebuilds it from root-owned proxy inputs.
local target=/tmp/nemoclaw-proxy-env.sh
local tmp
tmp="$(mktemp /tmp/nemoclaw-proxy-env.XXXXXX)"
Expand Down Expand Up @@ -343,7 +345,7 @@ prepare_observability_marker() {
tmp="$(mktemp "${target}.XXXXXX")"
printf '%s\n' '1' >"$tmp"
chmod 444 "$tmp"
mv -f "$tmp" "$target"
mv -fT -- "$tmp" "$target"
}

prepare_runtime_env
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ REPO="${REPO:-$(pwd)}"
CLI="${NEMOCLAW_E2E_CLI:-${REPO}/bin/nemoclaw.js}"
PROJECT_VENV="/sandbox/.nemoclaw-e2e-project-venv"
PROJECT_PYTHON="${PROJECT_VENV}/bin/python3"
OBSERVABILITY_MARKER_BEFORE="absent"

ok() { printf '%s\n' "${PREFIX}: OK ($*)"; }
info() { printf '%s\n' "${PREFIX}: $*"; }
Expand All @@ -37,6 +36,20 @@ observability_marker_value() {
2>/dev/null
}

observability_registry_state() {
SANDBOX_NAME="$SANDBOX_NAME" node - <<'NODE'
const fs = require("node:fs");
const path = require("node:path");
const registry = JSON.parse(
fs.readFileSync(path.join(process.env.HOME, ".nemoclaw", "sandboxes.json"), "utf8"),
);
const entry = registry.sandboxes?.[process.env.SANDBOX_NAME];
if (!entry || entry.agent !== "langchain-deepagents-code" ||
typeof entry.observabilityEnabled !== "boolean") process.exit(1);
process.stdout.write(entry.observabilityEnabled ? "enabled" : "disabled");
NODE
}

nemoclaw_cli() {
if [ -f "$CLI" ]; then
node "$CLI" "$@"
Expand Down Expand Up @@ -122,31 +135,18 @@ python_probe() {
sandbox_exec "$remote_cmd"
}

restore_observability_state() {
local marker_after restore_output state_invalid=0
marker_after="$(observability_marker_value || true)"
if [ "$OBSERVABILITY_MARKER_BEFORE" = "1" ] && [ "$marker_after" = "1" ]; then
pass "managed observability state remains enabled after policy-remove"
return 0
fi
if [ "$OBSERVABILITY_MARKER_BEFORE" = "1" ]; then
fail_test "managed observability marker was lost after policy-remove"
fi
state_invalid=1

if [ "$marker_after" != "1" ] \
&& ! restore_output="$(openshell sandbox exec --name "$SANDBOX_NAME" -- \
/usr/bin/env NEMOCLAW_OBSERVABILITY=1 \
/usr/local/bin/nemoclaw-start /usr/bin/true 2>&1)"; then
fail_test "could not restore managed observability for ordered cleanup: $restore_output"
verify_observability_state() {
local phase="$1" marker_state registry_state
if ! registry_state="$(observability_registry_state 2>&1)"; then
fail_test "could not read authoritative host observability intent $phase: ${registry_state:-no diagnostic}"
return 1
fi
if [ "$(observability_marker_value || true)" != "1" ]; then
fail_test "managed observability marker was not restored after policy-remove"
marker_state="$(observability_marker_value || true)"
if [ "$registry_state" != "enabled" ] || [ "$marker_state" != "1" ]; then
fail_test "observability state drifted $phase (registry=${registry_state:-unreadable}, marker=${marker_state:-unreadable})"
return 1
fi
pass "managed observability state restored for ordered cleanup"
return "$state_invalid"
pass "host registry and persistent marker preserve enabled observability $phase"
}

restore_tavily_denial() {
Expand All @@ -166,7 +166,7 @@ restore_tavily_denial() {
fi
fi

restore_observability_state || cleanup_status=1
verify_observability_state "after policy-remove" || cleanup_status=1
return "$cleanup_status"
}

Expand All @@ -192,23 +192,23 @@ fi

if [ "${NEMOCLAW_E2E_TAVILY_SELF_TEST:-}" = "restore-denial" ]; then
OBSERVABILITY_MARKER_FIXTURE="$(mktemp)"
OBSERVABILITY_MARKER_BEFORE=1
printf '%s\n' "1" >"$OBSERVABILITY_MARKER_FIXTURE"
trap 'rm -f "$OBSERVABILITY_MARKER_FIXTURE"' EXIT
observability_marker_value() {
cat "$OBSERVABILITY_MARKER_FIXTURE"
}
observability_registry_state() {
printf '%s' "${NEMOCLAW_E2E_OBSERVABILITY_REGISTRY_FIXTURE:-enabled}"
}
nemoclaw_cli() {
[[ "$*" == "$SANDBOX_NAME policy-remove tavily --yes" ]] || return 1
[ "${NEMOCLAW_E2E_TAVILY_REMOVE_FIXTURE:-ok}" = "ok" ] || return 1
if [ "${NEMOCLAW_E2E_TAVILY_MARKER_FIXTURE:-preserve}" = "lose" ]; then
printf '%s\n' "absent" >"$OBSERVABILITY_MARKER_FIXTURE"
fi
}
openshell() {
[[ "$*" == "sandbox exec --name $SANDBOX_NAME -- /usr/bin/env NEMOCLAW_OBSERVABILITY=1 /usr/local/bin/nemoclaw-start /usr/bin/true" ]] || return 1
printf '%s\n' "1" >"$OBSERVABILITY_MARKER_FIXTURE"
case "${NEMOCLAW_E2E_TAVILY_REMOVE_FIXTURE:-ok}" in
ok) ;;
clear-marker) printf '%s\n' "absent" >"$OBSERVABILITY_MARKER_FIXTURE" ;;
*) return 1 ;;
esac
}
verify_observability_state "before Tavily policy mutation" || exit 1
cleanup_status=0
NEMOCLAW_E2E_POLICY_SETTLE_SECONDS=0 restore_tavily_denial || cleanup_status=$?
[ "$(cat "$OBSERVABILITY_MARKER_FIXTURE")" = "1" ]
Expand All @@ -222,6 +222,8 @@ fi

info "Running Deep Agents Code Tavily opt-in check in sandbox: $SANDBOX_NAME"

verify_observability_state "before Tavily policy mutation" || exit 1

# shellcheck disable=SC2016 # command substitution must run inside the sandbox.
PYTHON_REAL="$(sandbox_exec 'readlink -f "$(command -v python3)"' || true)"
if [[ "$PYTHON_REAL" == /opt/venv/* ]]; then
Expand All @@ -246,10 +248,6 @@ APPLY_OUTPUT="$(nemoclaw_cli "$SANDBOX_NAME" policy-add tavily --yes 2>&1)" || {
printf '%s\n' "${PREFIX}: $PASSED passed, $FAILED failed"
exit 1
}
OBSERVABILITY_MARKER_BEFORE="$(observability_marker_value || true)"
if [ "$OBSERVABILITY_MARKER_BEFORE" != "1" ]; then
fail_test "managed observability marker is absent after policy-add"
fi
trap restore_tavily_denial EXIT
pass "tavily policy preset applies"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ PREFIX="11-deepagents-code-observability"
COLLECTOR_HOST="host.openshell.internal"
COLLECTOR_PORT=4318
DECOY_PORT=4319
OTLP_TRACE_URL="http://${COLLECTOR_HOST}:${COLLECTOR_PORT}/v1/traces"
CAPTURE_DIR="$(mktemp -d /tmp/nemoclaw-otlp-live.XXXXXX)"
COLLECTOR_LOG="${CAPTURE_DIR}/collector.log"
COLLECTOR_PID=""
OBSERVABILITY_POLICY_DIRTY=0
CAPTURE_SERVER="${REPO}/test/e2e/live/deepagents-otlp-capture-server.ts"
CONTRACT_HELPER="${REPO}/test/e2e/live/deepagents-observability-contract.ts"
TSX="${REPO}/node_modules/.bin/tsx"
Expand Down Expand Up @@ -51,12 +53,47 @@ sandbox_exec() {
openshell sandbox exec --name "$SANDBOX_NAME" -- bash -c "$1" 2>&1
}

observability_policy_state() {
"$CLI" "$SANDBOX_NAME" policy-list 2>&1 | "$TSX" "$CONTRACT_HELPER" policy-state
}

observability_marker_value() {
# shellcheck disable=SC2016 # marker expands inside the sandbox shell.
openshell sandbox exec --name "$SANDBOX_NAME" -- \
sh -c 'marker=/sandbox/.deepagents/.nemoclaw-observability-enabled; test -f "$marker" && ! test -L "$marker" && cat "$marker"' \
2>&1
}

restore_observability_policy() {
local state
[ "$OBSERVABILITY_POLICY_DIRTY" -eq 1 ] || return 0
state="$(observability_policy_state)" || return 1
case "$state" in
active) ;;
inactive)
"$CLI" "$SANDBOX_NAME" policy-add observability-otlp-local --yes >/dev/null 2>&1 \
|| return 1
[ "$(observability_policy_state)" = "active" ] || return 1
;;
*) return 1 ;;
esac
OBSERVABILITY_POLICY_DIRTY=0
Comment on lines +67 to +80

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Wait for policy enforcement to settle before probing.

The flow probes immediately after policy-remove and resumes positive checks immediately after policy-add. Unlike the Tavily cleanup at Line 158, no settlement delay protects these network assertions from asynchronous policy reloads.

Proposed fix
       "$CLI" "$SANDBOX_NAME" policy-add observability-otlp-local --yes >/dev/null 2>&1 \
         || return 1
       [ "$(observability_policy_state)" = "active" ] || return 1
+      sleep "${NEMOCLAW_E2E_POLICY_SETTLE_SECONDS:-5}"
       ;;
...
 [ "$policy_state" = "inactive" ] \
   || fail "removed observability policy is not exactly inactive (state: $policy_state)"
+sleep "${NEMOCLAW_E2E_POLICY_SETTLE_SECONDS:-5}"

Also applies to: 353-388

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/e2e/e2e-cloud-experimental/checks/11-deepagents-code-observability.sh`
around lines 67 - 80, Update the observability policy test flow around
policy-remove and policy-add, including restore_observability_policy, to wait
for policy enforcement to settle before probing network behavior. Reuse the
existing Tavily cleanup settlement-delay pattern rather than adding a different
synchronization mechanism, and apply the same delay to the additional affected
flow.

}

cleanup() {
local exit_status="$?"
trap - EXIT
if ! restore_observability_policy; then
printf '%s: policy cleanup failed; run: nemoclaw %q policy-add observability-otlp-local --yes\n' \
"$PREFIX" "$SANDBOX_NAME" >&2
exit_status=1
fi
if [ -n "$COLLECTOR_PID" ] && kill -0 "$COLLECTOR_PID" 2>/dev/null; then
kill "$COLLECTOR_PID" 2>/dev/null || true
wait "$COLLECTOR_PID" 2>/dev/null || true
fi
rm -rf "$CAPTURE_DIR"
exit "$exit_status"
}
trap cleanup EXIT

Expand Down Expand Up @@ -182,9 +219,7 @@ expect_blocked_without_capture() {
pass "$label is denied before the host collector"
}

policy_output="$("$CLI" "$SANDBOX_NAME" policy-list 2>&1)" || fail "could not inspect active policy"
policy_state="$(printf '%s\n' "$policy_output" | "$TSX" "$CONTRACT_HELPER" policy-state)" \
|| fail "could not parse observability policy state: $policy_output"
policy_state="$(observability_policy_state)" || fail "could not parse observability policy state"
[ "$policy_state" = "active" ] \
|| fail "observability-otlp-local is not exactly active (state: $policy_state)"

Expand All @@ -201,16 +236,12 @@ NODE
)"
[ "$registry_output" = "enabled" ] || fail "host registry does not record observability enabled"

# Expansion is intentionally deferred to the sandbox shell.
# shellcheck disable=SC2016
marker_output="$(openshell sandbox exec --name "$SANDBOX_NAME" -- \
sh -c 'marker=/sandbox/.deepagents/.nemoclaw-observability-enabled; test -f "$marker" && ! test -L "$marker" && cat "$marker"' \
2>&1)" || fail "managed observability marker is absent"
marker_output="$(observability_marker_value)" || fail "managed observability marker is absent"
[ "$marker_output" = "1" ] || fail "managed observability marker has an unexpected value"
pass "host registry, live policy, and sandbox marker agree on enabled observability"

allowed_output="$(sandbox_python_probe POST \
"http://${COLLECTOR_HOST}:${COLLECTOR_PORT}/v1/traces" \
"$OTLP_TRACE_URL" \
"$ALLOWED_PROBE")" || fail "allowed OTLP request failed: $allowed_output"
printf '%s\n' "$allowed_output" | grep -Fq 'REACHED:200' \
|| fail "allowed OTLP request lacked HTTP 200 evidence: $allowed_output"
Expand Down Expand Up @@ -271,7 +302,8 @@ from langchain.agents.middleware.types import ToolCallRequest
from deepagents_code import nemoclaw_observability as observability

tool_name, argument_marker, result_marker = sys.argv[1:]
os.environ["NEMOCLAW_OBSERVABILITY"] = "1"
if os.environ.get("NEMOCLAW_OBSERVABILITY") != "1":
raise RuntimeError("managed observability marker did not reach instrumentation")
if not observability.initialize_observability():
raise RuntimeError("managed observability did not initialize")
try:
Expand Down Expand Up @@ -305,14 +337,56 @@ run_deterministic_tool_trace() {
local encoded
encoded="$(tool_trace_source | base64 | tr -d '\n')"
"$CLI" "$SANDBOX_NAME" exec -- \
env NEMOCLAW_OBSERVABILITY=1 \
/usr/local/lib/nemoclaw/dcode-managed-exec \
env \
OTEL_SERVICE_NAME="$AMBIENT_CANARY" \
OTEL_RESOURCE_ATTRIBUTES="ambient.canary=${AMBIENT_CANARY}" \
/opt/venv/bin/python3 -I -c \
"import base64; exec(compile(base64.b64decode('${encoded}'), '<otlp-tool-trace>', 'exec'))" \
"$TOOL_NAME" "$TOOL_ARGUMENT" "$TOOL_RESULT" 2>&1
}

# Prove the sandbox-writable marker is not an authorization source. Remove only
# the host-managed OTLP preset, recreate the exact marker as the sandbox user,
# and exercise both the raw route and real managed instrumentation. The EXIT
# guard restores policy before any later sequential check can observe our test.
OBSERVABILITY_POLICY_DIRTY=1
remove_policy_output="$(
"$CLI" "$SANDBOX_NAME" policy-remove observability-otlp-local --yes 2>&1
)" || fail "could not remove observability policy for the negative proof: $remove_policy_output"
policy_state="$(observability_policy_state)" \
|| fail "could not inspect observability policy after removal"
[ "$policy_state" = "inactive" ] \
|| fail "removed observability policy is not exactly inactive (state: $policy_state)"

# shellcheck disable=SC2016 # marker expands inside the sandbox shell.
marker_recreate_output="$(openshell sandbox exec --name "$SANDBOX_NAME" -- \
sh -c 'marker=/sandbox/.deepagents/.nemoclaw-observability-enabled; rm -f "$marker"; printf "%s\n" 1 >"$marker"; chmod 444 "$marker"' \
2>&1)" || fail "sandbox user could not recreate the observability marker: $marker_recreate_output"
marker_output="$(observability_marker_value)" \
|| fail "sandbox-created observability marker is absent or unsafe"
[ "$marker_output" = "1" ] || fail "sandbox-created observability marker has an unexpected value"

expect_blocked_without_capture \
"sandbox-created marker without host observability policy" POST "$OTLP_TRACE_URL"

before_untrusted_trace="$(request_count)"
untrusted_trace_output="$(run_deterministic_tool_trace)" \
|| fail "marker-driven instrumentation failed before its denial proof: $untrusted_trace_output"
printf '%s\n' "$untrusted_trace_output" | grep -Fq 'TOOL_TRACE_OK' \
|| fail "marker-driven instrumentation lacked completion evidence: $untrusted_trace_output"
sleep 2
after_untrusted_trace="$(request_count)"
[ "$after_untrusted_trace" = "$before_untrusted_trace" ] \
|| fail "marker-driven instrumentation exported without host observability policy"
pass "marker-driven instrumentation cannot export without host observability policy"

restore_observability_policy || fail "could not restore observability policy after the negative proof"
marker_output="$(observability_marker_value)" \
|| fail "managed observability marker was lost while restoring policy"
[ "$marker_output" = "1" ] || fail "managed observability marker changed while restoring policy"
pass "host observability policy is restored before positive trace checks"

direct_output="$(run_dcode_direct)" || fail "direct-exec dcode observability turn failed: $direct_output"
printf '%s\n' "$direct_output" | grep -Fq "$DIRECT_RESPONSE" \
|| fail "direct-exec dcode response omitted its requested marker"
Expand Down Expand Up @@ -361,4 +435,4 @@ done
|| fail "captured OTLP contract did not become valid: $validation_output"

pass "decoded OTLP associates model/tool content and excludes ambient exporter configuration"
printf '%s: 11 passed, 0 failed\n' "$PREFIX"
printf '%s: 14 passed, 0 failed\n' "$PREFIX"
Original file line number Diff line number Diff line change
Expand Up @@ -259,18 +259,18 @@ main() {
[ -x "$CLI" ] || fail "NemoClaw CLI is not executable at $CLI"
[ -x "$NETWORK_BOUNDARY_CHECK" ] || fail "network boundary check is not executable"
[ -x "$CREDENTIAL_BOUNDARY_CHECK" ] || fail "credential boundary check is not executable"
command -v expect >/dev/null 2>&1 || fail "expect is required for the DCode autorun TUI check"
command -v node >/dev/null 2>&1 || fail "node is required to inspect status JSON"
is_positive_integer "$TUI_TIMEOUT" \
|| fail "DEEPAGENTS_AUTORUN_TIMEOUT must be a positive integer"

# The generic cloud-onboard target runs shared checks against OpenClaw. Typed
# DCode targets reject this SKIP through the required-check wrapper.
if ! sandbox_exec "test -d /sandbox/.deepagents && command -v dcode >/dev/null 2>&1" >/dev/null; then
printf '%s: SKIP: sandbox %q is not a Deep Agents Code sandbox\n' "$PREFIX" "$SANDBOX_NAME"
exit 0
fi

command -v expect >/dev/null 2>&1 || fail "expect is required for the DCode autorun TUI check"
command -v node >/dev/null 2>&1 || fail "node is required to inspect status JSON"
is_positive_integer "$TUI_TIMEOUT" \
|| fail "DEEPAGENTS_AUTORUN_TIMEOUT must be a positive integer"

trap cleanup_probe_files EXIT
cleanup_probe_files

Expand Down
3 changes: 3 additions & 0 deletions test/e2e/support/deepagents-observability-contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,9 @@ describe("Deep Agents observability policy proof", () => {
" ● observability-otlp-local [from balanced tier] — host-local OTLP export\n",
),
).toBe("active");
expect(
observabilityPresetState(" ○ observability-otlp-local — host-local OTLP export\n"),
).toBe("inactive");
expect(
observabilityPresetState(
" ○ observability-otlp-local — host-local OTLP export (recorded locally, not active on gateway)\n",
Expand Down
Loading
Loading