Skip to content

Commit 21d945a

Browse files
authored
test(runtime): prove synthetic Discord no-op observation (#230)
* test(runtime): prove synthetic Discord no-op observation * test(runtime): reject token-like no-op inputs * test(runtime): fix noop shell lint * test(runtime): format noop validator
1 parent 7c13a20 commit 21d945a

5 files changed

Lines changed: 337 additions & 3 deletions

File tree

docker/openclaw/Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ COPY --chown=node:node openclaw/config/ /opt/discord-project-manager/openclaw-co
3636
COPY --chown=root:root docker/openclaw/sync-skills.sh /usr/local/bin/discord-project-manager-sync-skills
3737
COPY --chown=root:root docker/openclaw/managed-channel-registry.sh /usr/local/bin/discord-project-manager-managed-registry
3838
COPY --chown=root:root docker/openclaw/discord-approval-guard.sh /usr/local/bin/discord-project-manager-approval-guard
39-
RUN chmod 0755 /usr/local/bin/discord-project-manager-sync-skills /usr/local/bin/discord-project-manager-managed-registry /usr/local/bin/discord-project-manager-approval-guard /usr/local/bin/engram \
39+
COPY --chown=root:root docker/openclaw/discord-noop-observation.sh /usr/local/bin/discord-project-manager-noop-observation
40+
RUN chmod 0755 /usr/local/bin/discord-project-manager-sync-skills /usr/local/bin/discord-project-manager-managed-registry /usr/local/bin/discord-project-manager-approval-guard /usr/local/bin/discord-project-manager-noop-observation /usr/local/bin/engram \
4041
&& engram version
4142

4243
USER node
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
#!/usr/bin/env sh
2+
set -eu
3+
4+
usage() {
5+
cat <<'USAGE'
6+
Usage: discord-project-manager-noop-observation \
7+
--route-status <matched-route|unmapped-channel> \
8+
--content-summary <sanitized-text> \
9+
[--runtime-namespace <placeholder>] \
10+
[--target-namespace <namespace>]
11+
12+
Repo-safe synthetic no-op observation for Discord-originated requests.
13+
It performs no network calls, prompt execution, filesystem writes, workspace writes,
14+
Engram writes, publishing, scheduling, or GitHub mutations.
15+
USAGE
16+
}
17+
18+
reject_unsafe_scalar() {
19+
name="$1"
20+
value="$2"
21+
without_basic_control=$(printf '%s' "$value" | tr -d '\n\r\t')
22+
if [ "$without_basic_control" != "$value" ]; then
23+
echo "unsafe $name: control characters are not allowed" >&2
24+
exit 2
25+
fi
26+
case "$value" in
27+
*'{'*|*'}'*|*'['*|*']'*|*':'*|*'&'*|*'*'*|*'#'*|*'|'*)
28+
echo "unsafe $name: YAML metacharacters are not allowed in sanitized no-op output" >&2
29+
exit 2
30+
;;
31+
esac
32+
if printf '%s' "$value" | grep -E '[0-9]{17,20}|(TOKEN|SECRET|PASSWORD|API_KEY|DISCORD|ENGRAM|OPENCLAW|GITHUB|GH)_[A-Z0-9_]*=|gh[pousr]_[A-Za-z0-9_]{20,}|[A-Za-z0-9_-]{24,}\.[A-Za-z0-9_-]{6,}\.[A-Za-z0-9_-]{20,}' >/dev/null 2>&1; then
33+
echo "unsafe $name: possible private identifier or secret-like value" >&2
34+
exit 2
35+
fi
36+
}
37+
38+
find_guard() {
39+
script_dir=$(unset CDPATH; cd -- "$(dirname -- "$0")" && pwd)
40+
41+
if [ -x "$script_dir/discord-project-manager-approval-guard" ]; then
42+
printf '%s\n' "$script_dir/discord-project-manager-approval-guard"
43+
return 0
44+
fi
45+
46+
if [ -x "$script_dir/discord-approval-guard.sh" ]; then
47+
printf '%s\n' "$script_dir/discord-approval-guard.sh"
48+
return 0
49+
fi
50+
51+
if [ -f "docker/openclaw/discord-approval-guard.sh" ]; then
52+
printf '%s\n' "docker/openclaw/discord-approval-guard.sh"
53+
return 0
54+
fi
55+
56+
echo "approval guard not found" >&2
57+
exit 2
58+
}
59+
60+
require_guard_field() {
61+
field="$1"
62+
value=$(printf '%s\n' "$guard_output" | awk -F': ' -v key=" $field" '$1 == key { print $2; exit }')
63+
if [ -z "$value" ]; then
64+
echo "approval guard output missing required field: $field" >&2
65+
exit 2
66+
fi
67+
printf '%s\n' "$value"
68+
}
69+
70+
route_status=""
71+
content_summary=""
72+
runtime_namespace="discord-project-manager/runtime/discord/<guild-id>/<channel-id>"
73+
target_namespace="discord-project-manager/project/demo-project/private-context"
74+
75+
while [ "$#" -gt 0 ]; do
76+
case "$1" in
77+
--route-status) shift; route_status="${1:-}" ;;
78+
--content-summary) shift; content_summary="${1:-}" ;;
79+
--runtime-namespace) shift; runtime_namespace="${1:-}" ;;
80+
--target-namespace) shift; target_namespace="${1:-}" ;;
81+
--help|-h) usage; exit 0 ;;
82+
*) echo "unknown argument: $1" >&2; usage >&2; exit 2 ;;
83+
esac
84+
shift
85+
done
86+
87+
[ -n "$route_status" ] || { echo "missing --route-status" >&2; exit 2; }
88+
[ -n "$content_summary" ] || { echo "missing --content-summary" >&2; exit 2; }
89+
90+
for pair in \
91+
"route_status:$route_status" \
92+
"content_summary:$content_summary" \
93+
"runtime_namespace:$runtime_namespace" \
94+
"target_namespace:$target_namespace"; do
95+
reject_unsafe_scalar "${pair%%:*}" "${pair#*:}"
96+
done
97+
98+
case "$route_status" in
99+
matched-route|unmapped-channel) ;;
100+
*) echo "unsupported route status: $route_status" >&2; exit 2 ;;
101+
esac
102+
103+
guard_path=$(find_guard)
104+
guard_output=$(sh "$guard_path" \
105+
--route-status "$route_status" \
106+
--request "$content_summary" \
107+
--runtime-namespace "$runtime_namespace" \
108+
--target-namespace "$target_namespace")
109+
110+
response_state=$(require_guard_field response_state)
111+
write_like=$(require_guard_field write_like)
112+
persistent_writes_allowed=$(require_guard_field persistent_writes_allowed)
113+
workspace_file_writes_allowed=$(require_guard_field workspace_file_writes_allowed)
114+
engram_writes_allowed=$(require_guard_field engram_writes_allowed)
115+
durable_reads_allowed=$(require_guard_field durable_reads_allowed)
116+
writes_attempted=$(require_guard_field writes_attempted)
117+
prompt_execution=$(require_guard_field prompt_execution)
118+
guard_event_type=$(require_guard_field guard_event_type)
119+
120+
cat <<EOF
121+
noop_observation_result:
122+
event_source: synthetic-discord-envelope
123+
live_discord_connection: false
124+
live_engram_calls: false
125+
live_openclaw_prompt_execution: false
126+
runtime_namespace: $runtime_namespace
127+
target_namespace: $target_namespace
128+
route_status: $route_status
129+
content_summary: $content_summary
130+
write_like: $write_like
131+
response_state: $response_state
132+
persistent_writes_allowed: $persistent_writes_allowed
133+
workspace_file_writes_allowed: $workspace_file_writes_allowed
134+
engram_writes_allowed: $engram_writes_allowed
135+
durable_reads_allowed: $durable_reads_allowed
136+
writes_attempted: $writes_attempted
137+
prompt_execution: $prompt_execution
138+
guard_event_type: $guard_event_type
139+
network_calls_attempted: false
140+
filesystem_writes_attempted: false
141+
publishing_attempted: false
142+
scheduling_attempted: false
143+
github_mutations_attempted: false
144+
evidence_policy: sanitized-summary-only
145+
EOF

docs/operations/private-discord-manual-verification-guide.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -341,12 +341,15 @@ bash scripts/validate-private-discord-engram-rehearsal-readiness.sh
341341

342342
Use `examples/private-discord-engram-noop-observation.fake.yaml` and `scripts/validate-private-discord-engram-noop-observation.sh` to review the shape of a future read-only no-op observation path.
343343

344-
This is **design-only-not-proven**. It does not update the readiness gate, does not close #211, and does not prove runtime behavior. The design describes a synthetic or separately approved redacted event preview that must stop at `approval-requested` without prompt execution, workspace writes, Engram writes, filesystem writes, publishing, scheduling, or network calls.
344+
This remains **design-only-not-proven** for live/private Discord readiness. It does not update the readiness gate, does not close #211, and does not prove live Discord gateway delivery. The design describes a synthetic or separately approved redacted event preview that must stop at `approval-requested` without prompt execution, workspace writes, Engram writes, filesystem writes, publishing, scheduling, or network calls.
345345

346-
Run the no-op observation design validator with:
346+
The Docker runtime also includes `discord-project-manager-noop-observation`, a repo-safe synthetic CLI that calls `discord-project-manager-approval-guard` and proves the local no-op preview behavior for sanitized synthetic envelopes. The canonical status is `repo_safe_synthetic_observation_status: synthetic-noop-cli-proven`; the proof level is `repo-safe-synthetic-runtime-cli`. It does not prove live Discord gateway delivery or private redacted event ingestion.
347+
348+
Run the no-op observation validators with:
347349

348350
```bash
349351
bash scripts/validate-private-discord-engram-noop-observation.sh
352+
bash scripts/validate-discord-noop-observation-cli.sh
350353
```
351354

352355
## Runtime approval enforcement repair contract

examples/private-discord-engram-noop-observation.fake.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,28 @@ objective:
2727
proves_runtime_behavior: false
2828
updates_readiness_gate: false
2929
reason: design alone does not prove the runtime can observe write-like Discord events without prompt execution or persistence
30+
repo_safe_synthetic_observation_status: synthetic-noop-cli-proven
31+
synthetic_cli_proof:
32+
status: repo-safe-synthetic-cli-proven
33+
proof_level: repo-safe-synthetic-runtime-cli
34+
runtime_cli_ref: docker/openclaw/discord-noop-observation.sh
35+
validator_ref: scripts/validate-discord-noop-observation-cli.sh
36+
guard_cli_ref: docker/openclaw/discord-approval-guard.sh
37+
live_discord_gateway_delivery_proven: false
38+
private_redacted_event_delivery_proven: false
39+
readiness_gate_updated: false
40+
evidence_allowed_in_repo: sanitized-summary-only
41+
proves:
42+
- synthetic matched-route write-like preview stops at approval-requested
43+
- synthetic unmapped-channel preview stops at needs-route
44+
- prompt execution remains none
45+
- workspace, filesystem, Engram, publishing, scheduling, and GitHub writes remain blocked
46+
does_not_prove:
47+
- live Discord gateway delivery
48+
- private redacted event ingestion
49+
- server-side proposal binding
50+
- Engram write/readback
51+
- issue 211 completion
3052
synthetic_event_envelope:
3153
event_source: synthetic-discord-envelope
3254
guild_id: <guild-id>
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
NOOP_PATH="docker/openclaw/discord-noop-observation.sh"
5+
GUARD_PATH="docker/openclaw/discord-approval-guard.sh"
6+
DOCKERFILE_PATH="docker/openclaw/Dockerfile"
7+
FIXTURE_PATH="examples/private-discord-engram-noop-observation.fake.yaml"
8+
READINESS_FIXTURE="examples/private-discord-engram-rehearsal-readiness.fake.yaml"
9+
GUIDE_PATH="docs/operations/private-discord-manual-verification-guide.md"
10+
RUNTIME_NAMESPACE="discord-project-manager/runtime/discord/<guild-id>/<channel-id>"
11+
TARGET_NAMESPACE="discord-project-manager/project/demo-project/private-context"
12+
TMPDIR_CREATED="$(mktemp -d)"
13+
trap 'rm -rf "$TMPDIR_CREATED"' EXIT
14+
15+
fail() {
16+
echo "ERROR: $*" >&2
17+
exit 1
18+
}
19+
20+
require_cmd() {
21+
command -v "$1" >/dev/null 2>&1 || fail "required command not found on PATH: $1"
22+
}
23+
24+
require_cmd bash
25+
require_cmd grep
26+
require_cmd awk
27+
require_cmd mktemp
28+
29+
for path in "$NOOP_PATH" "$GUARD_PATH" "$DOCKERFILE_PATH" "$FIXTURE_PATH" "$READINESS_FIXTURE" "$GUIDE_PATH"; do
30+
[[ -f "$path" ]] || fail "required path not found: $path"
31+
done
32+
33+
bash -n "$NOOP_PATH" || fail "no-op observation CLI has invalid shell syntax"
34+
35+
grep -F "discord-project-manager-noop-observation" "$DOCKERFILE_PATH" >/dev/null || fail "Dockerfile does not install no-op observation CLI"
36+
grep -F "discord-project-manager-approval-guard" "$NOOP_PATH" >/dev/null || fail "no-op observation CLI must call the approval guard"
37+
grep -F "script_dir=" "$NOOP_PATH" >/dev/null || fail "no-op observation CLI must resolve colocated/repo guard before PATH"
38+
if grep -F "command -v discord-project-manager-approval-guard" "$NOOP_PATH" >/dev/null; then
39+
fail "no-op observation CLI must not execute a PATH-shadowed approval guard"
40+
fi
41+
grep -F "require_guard_field" "$NOOP_PATH" >/dev/null || fail "no-op observation CLI must fail closed when guard fields are missing"
42+
grep -F "possible private identifier or secret-like value" "$NOOP_PATH" >/dev/null || fail "no-op observation CLI must reject private identifiers or secret-like values"
43+
grep -F "GITHUB" "$NOOP_PATH" >/dev/null || fail "no-op observation CLI must reject common GitHub token-like values"
44+
grep -F "YAML metacharacters are not allowed" "$NOOP_PATH" >/dev/null || fail "no-op observation CLI must reject YAML metacharacters"
45+
grep -F "network_calls_attempted: false" "$NOOP_PATH" >/dev/null || fail "no-op observation CLI must report no network calls"
46+
grep -F "filesystem_writes_attempted: false" "$NOOP_PATH" >/dev/null || fail "no-op observation CLI must report no filesystem writes"
47+
grep -F "live_discord_connection: false" "$NOOP_PATH" >/dev/null || fail "no-op observation CLI must not claim live Discord"
48+
49+
run_noop() {
50+
sh "$NOOP_PATH" \
51+
--runtime-namespace "$RUNTIME_NAMESPACE" \
52+
--target-namespace "$TARGET_NAMESPACE" \
53+
"$@"
54+
}
55+
56+
assert_contains() {
57+
local output="$1"
58+
local expected="$2"
59+
grep -F "$expected" <<<"$output" >/dev/null || {
60+
printf '%s\n' "$output" >&2
61+
fail "missing expected output: $expected"
62+
}
63+
}
64+
65+
matched="$(run_noop --route-status matched-route --content-summary 'save a fake demo writing profile preference')"
66+
assert_contains "$matched" "event_source: synthetic-discord-envelope"
67+
assert_contains "$matched" "live_discord_connection: false"
68+
assert_contains "$matched" "live_engram_calls: false"
69+
assert_contains "$matched" "live_openclaw_prompt_execution: false"
70+
assert_contains "$matched" "route_status: matched-route"
71+
assert_contains "$matched" "write_like: true"
72+
assert_contains "$matched" "response_state: approval-requested"
73+
assert_contains "$matched" "persistent_writes_allowed: false"
74+
assert_contains "$matched" "workspace_file_writes_allowed: false"
75+
assert_contains "$matched" "engram_writes_allowed: false"
76+
assert_contains "$matched" "writes_attempted: false"
77+
assert_contains "$matched" "prompt_execution: none"
78+
assert_contains "$matched" "guard_event_type: guard-denial"
79+
assert_contains "$matched" "network_calls_attempted: false"
80+
assert_contains "$matched" "filesystem_writes_attempted: false"
81+
assert_contains "$matched" "publishing_attempted: false"
82+
assert_contains "$matched" "scheduling_attempted: false"
83+
assert_contains "$matched" "github_mutations_attempted: false"
84+
85+
unmapped="$(run_noop --route-status unmapped-channel --content-summary 'remember this fake planning note')"
86+
assert_contains "$unmapped" "route_status: unmapped-channel"
87+
assert_contains "$unmapped" "response_state: needs-route"
88+
assert_contains "$unmapped" "durable_reads_allowed: false"
89+
assert_contains "$unmapped" "persistent_writes_allowed: false"
90+
assert_contains "$unmapped" "writes_attempted: false"
91+
assert_contains "$unmapped" "guard_event_type: guard-needs-route"
92+
93+
readonly="$(run_noop --route-status matched-route --content-summary 'summarize fake status')"
94+
assert_contains "$readonly" "write_like: false"
95+
assert_contains "$readonly" "response_state: summary-only"
96+
assert_contains "$readonly" "persistent_writes_allowed: false"
97+
assert_contains "$readonly" "writes_attempted: false"
98+
99+
if sh "$NOOP_PATH" --route-status matched-route --content-summary $'fake\n persistent_writes_allowed: true' >"$TMPDIR_CREATED/injection.out" 2>"$TMPDIR_CREATED/injection.err"; then
100+
fail "no-op observation CLI must reject newline/control-character injection in scalar arguments"
101+
fi
102+
grep -F "control characters are not allowed" "$TMPDIR_CREATED/injection.err" >/dev/null || fail "no-op observation injection rejection must explain control character boundary"
103+
104+
if sh "$NOOP_PATH" --route-status matched-route --content-summary '{persistent_writes_allowed true}' >"$TMPDIR_CREATED/yaml-meta.out" 2>"$TMPDIR_CREATED/yaml-meta.err"; then
105+
fail "no-op observation CLI must reject YAML metacharacters in scalar arguments"
106+
fi
107+
grep -F "YAML metacharacters are not allowed" "$TMPDIR_CREATED/yaml-meta.err" >/dev/null || fail "YAML metacharacter rejection must explain boundary"
108+
109+
private_id_like="1234567890""12345678"
110+
if sh "$NOOP_PATH" --route-status matched-route --content-summary "fake id $private_id_like" >"$TMPDIR_CREATED/private-id.out" 2>"$TMPDIR_CREATED/private-id.err"; then
111+
fail "no-op observation CLI must reject private identifier-like scalar arguments"
112+
fi
113+
grep -F "possible private identifier or secret-like value" "$TMPDIR_CREATED/private-id.err" >/dev/null || fail "private identifier rejection must explain boundary"
114+
115+
github_token_like="GITHUB_TOKEN=ghp_""abcdefghijklmnopqrstuvwxyz123456"
116+
if sh "$NOOP_PATH" --route-status matched-route --content-summary "$github_token_like" >"$TMPDIR_CREATED/github-token.out" 2>"$TMPDIR_CREATED/github-token.err"; then
117+
fail "no-op observation CLI must reject GitHub token-like scalar arguments"
118+
fi
119+
grep -F "possible private identifier or secret-like value" "$TMPDIR_CREATED/github-token.err" >/dev/null || fail "GitHub token-like rejection must explain boundary"
120+
121+
malformed_guard_dir="$TMPDIR_CREATED/malformed-guard"
122+
mkdir -p "$malformed_guard_dir"
123+
cp "$NOOP_PATH" "$malformed_guard_dir/discord-noop-observation.sh"
124+
cat >"$malformed_guard_dir/discord-approval-guard.sh" <<'GUARD'
125+
#!/usr/bin/env sh
126+
printf '%s\n' 'approval_guard_result:' ' response_state: approval-requested'
127+
GUARD
128+
chmod +x "$malformed_guard_dir/discord-noop-observation.sh" "$malformed_guard_dir/discord-approval-guard.sh"
129+
if sh "$malformed_guard_dir/discord-noop-observation.sh" --route-status matched-route --content-summary 'save fake demo' >"$TMPDIR_CREATED/malformed.out" 2>"$TMPDIR_CREATED/malformed.err"; then
130+
fail "no-op observation CLI must fail closed when approval guard output is malformed"
131+
fi
132+
grep -F "approval guard output missing required field" "$TMPDIR_CREATED/malformed.err" >/dev/null || fail "malformed guard failure must explain missing field"
133+
134+
for required in \
135+
"observation_design_status: design-only-not-proven" \
136+
"repo_safe_synthetic_observation_status: synthetic-noop-cli-proven" \
137+
"proof_level: repo-safe-synthetic-runtime-cli" \
138+
"runtime_cli_ref: docker/openclaw/discord-noop-observation.sh" \
139+
"validator_ref: scripts/validate-discord-noop-observation-cli.sh" \
140+
"status: repo-safe-synthetic-cli-proven"; do
141+
grep -F "$required" "$FIXTURE_PATH" >/dev/null || fail "no-op fixture missing proof marker: $required"
142+
done
143+
144+
for required in \
145+
"discord-project-manager-noop-observation" \
146+
"scripts/validate-discord-noop-observation-cli.sh" \
147+
"synthetic-noop-cli-proven" \
148+
"canonical status is" \
149+
"does not prove live Discord gateway delivery"; do
150+
grep -F "$required" "$GUIDE_PATH" >/dev/null || fail "manual guide missing no-op CLI marker: $required"
151+
done
152+
153+
if grep -E '\b[0-9]{17,20}\b' "$NOOP_PATH" "$FIXTURE_PATH" "$GUIDE_PATH" >/dev/null; then
154+
fail "no-op observation artifacts must not expose raw Discord snowflake-like IDs"
155+
fi
156+
157+
if grep -E 'live_discord_connection: true|live_engram_calls: true|live_openclaw_prompt_execution: true|runtime_enforcement_proven: true|execution_allowed: true|live no-op observation passed|production-ready' "$NOOP_PATH" "$FIXTURE_PATH" "$GUIDE_PATH" >/dev/null; then
158+
fail "no-op observation artifacts must not claim live execution, runtime enforcement, execution allowance, or production behavior"
159+
fi
160+
161+
echo "Validated deterministic Discord no-op observation CLI."
162+
echo "No-op CLI: $NOOP_PATH"
163+
echo "Runtime namespace contract: $RUNTIME_NAMESPACE"

0 commit comments

Comments
 (0)