Skip to content

Commit e79c85f

Browse files
author
kai777
committed
Add public-safe dry-run workflow contract
1 parent ca21108 commit e79c85f

6 files changed

Lines changed: 290 additions & 2 deletions

File tree

.github/workflows/public-ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ jobs:
5555
- name: Public status-read example test
5656
run: pnpm run test:status-read-example
5757

58+
- name: Public dry-run workflow test
59+
run: pnpm run test:dry-run
60+
5861
- name: Dispatch prompt generation test
5962
run: node scripts/test-dispatch-prompt-generate.mjs
6063

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ Public smoke check:
177177
- [Minimal Joint-Agent Flow](docs/examples/minimal-joint-agent-flow.md)
178178
- [Public-Safe Status-Read Workflow](docs/workflows/public-safe-status-read-workflow.md)
179179
- [Public-Safe Status-Read Example](docs/examples/public-safe-status-read-example.md)
180+
- [Public-Safe Dry-Run Workflow](docs/workflows/public-safe-dry-run-workflow.md)
180181
- [Technical Tool Reference](docs/han-agent-bus/technical-tool-reference.md)
181182
- [Safety Model](docs/han-agent-bus/safety-model.md)
182183
- [Public Roadmap](docs/han-agent-bus/public-roadmap.md)
@@ -256,7 +257,7 @@ The next public direction should stay narrow:
256257

257258
1. stronger documentation for tool contracts
258259
2. more example flows
259-
3. public-safe dry-run workflow
260+
3. public-safe dry-run mock implementation example
260261

261262
The project should not jump straight into unrestricted live automation.
262263

docs/quick-start.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,10 @@ Public status-read implementation example check:
138138

139139
pnpm run test:status-read-example
140140

141+
Public dry-run workflow contract check:
142+
143+
pnpm run test:dry-run
144+
141145
These checks are intended to validate public code paths and safety constraints.
142146

143147
They should not require private runtime artifacts.
@@ -175,6 +179,7 @@ Start with:
175179
- [Public Roadmap](han-agent-bus/public-roadmap.md)
176180
- [Public-Safe Status-Read Workflow](workflows/public-safe-status-read-workflow.md)
177181
- [Public-Safe Status-Read Example](examples/public-safe-status-read-example.md)
182+
- [Public-Safe Dry-Run Workflow](workflows/public-safe-dry-run-workflow.md)
178183
- [Security Policy](../SECURITY.md)
179184
- [Contributing Guide](../CONTRIBUTING.md)
180185

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
# Public-Safe Dry-Run Workflow
2+
3+
This document defines a public-safe dry-run workflow contract for HAN Agent OS.
4+
5+
It is intentionally narrow.
6+
7+
It does not perform live execution.
8+
9+
It does not mutate files.
10+
11+
It does not run arbitrary shell commands.
12+
13+
It does not launch browser automation.
14+
15+
It does not require secrets.
16+
17+
It does not call live OpenClaw.
18+
19+
It only defines the safe shape of a future dry-run request, response, and receipt.
20+
21+
---
22+
23+
## Purpose
24+
25+
The goal is to show how HAN Agent OS should handle a proposed action before any real execution happens.
26+
27+
A dry-run is not a live action.
28+
29+
A dry-run should answer:
30+
31+
What would be done if this were approved later?
32+
33+
It should not actually do the action.
34+
35+
---
36+
37+
## Difference between status-read and dry-run
38+
39+
| Workflow | Purpose | Performs action |
40+
|---|---|---|
41+
| status-read | Read current public-safe status | No |
42+
| dry-run | Simulate or describe a proposed action | No |
43+
| live execution | Actually perform the approved action | Yes, but not in this public workflow |
44+
45+
This document covers dry-run only.
46+
47+
---
48+
49+
## Roles
50+
51+
| Role | System | Responsibility |
52+
|---|---|---|
53+
| Human Operator | Human | Requests a dry-run preview |
54+
| Brain | Hermes | Understands intent and classifies the request |
55+
| Control bus | HAN Agent OS | Enforces dry-run boundary |
56+
| Hands | OpenClaw | Not called live in this public workflow |
57+
| Code specialist | Codex | Not required unless the dry-run concerns code changes |
58+
| Receipt layer | HAN Agent OS | Records the simulated plan and confirms no live action occurred |
59+
60+
---
61+
62+
## Example request
63+
64+
Human:
65+
66+
Dry-run what would happen if OpenClaw checked local agent status.
67+
68+
Hermes should classify this as:
69+
70+
task_class: dry_run
71+
mutation_allowed: false
72+
live_task_execution_allowed: false
73+
file_write_allowed: false
74+
shell_execution_allowed: false
75+
browser_automation_allowed: false
76+
credential_access_allowed: false
77+
repeated_execution_allowed: false
78+
chained_execution_allowed: false
79+
80+
The important distinction:
81+
82+
Dry-run planning is allowed.
83+
Live execution is not allowed.
84+
85+
---
86+
87+
## Allowed dry-run behavior
88+
89+
A public-safe dry-run may:
90+
91+
- describe the intended action
92+
- validate whether the request is narrow enough
93+
- identify the target system
94+
- identify whether human approval would be needed
95+
- produce a simulated command mapping
96+
- produce a simulated receipt shape
97+
- recommend stop, revise, or request approval
98+
99+
It must stay descriptive.
100+
101+
It must not execute the action.
102+
103+
---
104+
105+
## Forbidden dry-run behavior
106+
107+
A public-safe dry-run must not:
108+
109+
- execute tasks
110+
- mutate files
111+
- run arbitrary shell commands
112+
- launch browser automation
113+
- persist credentials
114+
- persist browser sessions
115+
- persist API keys
116+
- call live OpenClaw
117+
- call live Codex execution
118+
- expose raw private configuration
119+
- expose raw stdout or stderr logs
120+
- expose private runtime receipts
121+
- repeat automatically
122+
- chain into a live action
123+
124+
If a dry-run request needs any forbidden behavior, it must be blocked and escalated for human review.
125+
126+
---
127+
128+
## Expected routing
129+
130+
The routing should be:
131+
132+
Human Operator
133+
-> Hermes classifies the request as dry_run
134+
-> HAN Agent OS checks the dry-run boundary
135+
-> HAN Agent OS produces a simulated action plan
136+
-> No live OpenClaw call is made
137+
-> No Codex execution is made unless explicitly scoped as code planning
138+
-> HAN Agent OS records a dry-run receipt
139+
-> Human decides whether to stop, revise, or approve a later action
140+
141+
Dry-run does not automatically authorize live execution.
142+
143+
---
144+
145+
## Minimal dry-run receipt
146+
147+
A dry-run receipt should include:
148+
149+
operation_type: dry_run
150+
target: openclaw
151+
requested_action: status_check_preview
152+
mutation_performed: false
153+
live_task_executed: false
154+
file_write_performed: false
155+
shell_execution_performed: false
156+
browser_automation_performed: false
157+
credential_access_performed: false
158+
openclaw_live_call_performed: false
159+
codex_execution_performed: false
160+
repeated_execution_performed: false
161+
chained_execution_performed: false
162+
simulated_plan_available: true
163+
next_step: human approval required before any live action
164+
165+
This receipt proves both the simulated plan and the fact that no live action occurred.
166+
167+
---
168+
169+
## Human-facing response shape
170+
171+
A good response should look like this:
172+
173+
Dry-run completed.
174+
Proposed target: OpenClaw.
175+
Proposed action: status check preview.
176+
No task execution was performed.
177+
No files were changed.
178+
No shell command was run.
179+
No browser automation was used.
180+
No credentials were accessed.
181+
No live OpenClaw call was made.
182+
Next step: stop, revise the plan, or request explicit approval.
183+
184+
The response should not imply that OpenClaw was actually called.
185+
186+
The response should not imply that a live result was obtained.
187+
188+
---
189+
190+
## Why this belongs in v0.2.2
191+
192+
The v0.2.0 line introduced a public-safe status-read workflow contract.
193+
194+
The v0.2.1 line added a public-safe status-read mock implementation example.
195+
196+
The next safe step is dry-run contract definition.
197+
198+
This creates a controlled bridge between read-only status and future execution planning without jumping into live automation.
199+
200+
---
201+
202+
## Final boundary
203+
204+
This workflow is a contract only.
205+
206+
It does not authorize live OpenClaw execution.
207+
208+
It does not authorize Codex code changes.
209+
210+
It does not authorize shell access.
211+
212+
It does not authorize browser automation.
213+
214+
It only defines a safe dry-run workflow shape.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
"start": "node dist/mcp-server.js",
1010
"test:public": "node scripts/test-public-smoke.mjs",
1111
"test:status-read": "node scripts/test-public-status-read-workflow.mjs",
12-
"test:status-read-example": "node scripts/test-public-status-read-example.mjs"
12+
"test:status-read-example": "node scripts/test-public-status-read-example.mjs",
13+
"test:dry-run": "node scripts/test-public-dry-run-workflow.mjs"
1314
},
1415
"keywords": [],
1516
"author": "",
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { existsSync, readFileSync } from 'node:fs';
2+
3+
function fail(message) {
4+
throw new Error(message);
5+
}
6+
7+
function assert(condition, message) {
8+
if (!condition) fail(message);
9+
}
10+
11+
const docPath = 'docs/workflows/public-safe-dry-run-workflow.md';
12+
13+
assert(existsSync(docPath), `missing workflow doc: ${docPath}`);
14+
15+
const text = readFileSync(docPath, 'utf8');
16+
17+
const requiredPhrases = [
18+
'public-safe dry-run',
19+
'does not perform live execution',
20+
'does not mutate files',
21+
'does not run arbitrary shell commands',
22+
'does not launch browser automation',
23+
'does not require secrets',
24+
'does not call live OpenClaw',
25+
'task_class: dry_run',
26+
'mutation_allowed: false',
27+
'live_task_execution_allowed: false',
28+
'file_write_allowed: false',
29+
'shell_execution_allowed: false',
30+
'browser_automation_allowed: false',
31+
'credential_access_allowed: false',
32+
'repeated_execution_allowed: false',
33+
'chained_execution_allowed: false',
34+
'operation_type: dry_run',
35+
'mutation_performed: false',
36+
'live_task_executed: false',
37+
'file_write_performed: false',
38+
'shell_execution_performed: false',
39+
'browser_automation_performed: false',
40+
'credential_access_performed: false',
41+
'openclaw_live_call_performed: false',
42+
'codex_execution_performed: false',
43+
'simulated_plan_available: true',
44+
'human approval required before any live action'
45+
];
46+
47+
for (const phrase of requiredPhrases) {
48+
assert(text.includes(phrase), `dry-run workflow doc missing required phrase: ${phrase}`);
49+
}
50+
51+
const forbiddenClaims = [
52+
'live execution is allowed automatically',
53+
'OpenClaw may be called live without approval',
54+
'shell commands may run without approval',
55+
'credentials may be persisted',
56+
'browser sessions may be persisted',
57+
'dry-run automatically authorizes live execution'
58+
];
59+
60+
for (const phrase of forbiddenClaims) {
61+
assert(!text.includes(phrase), `dry-run workflow doc contains forbidden claim: ${phrase}`);
62+
}
63+
64+
console.log('public_dry_run_workflow_result: pass');

0 commit comments

Comments
 (0)