REQ-C-024: GUI-Launching Commands Declare Headless Behavior
Tier: Command Contract | Priority: P1
Source: §64 Headless Display and GUI Launch Blocking
Addresses: Severity: Critical / Token Spend: High / Time: Critical / Context: Low
Description
Any command that launches a GUI application, opens a browser, or invokes a display-requiring subprocess MUST declare gui_operations: [<list of operation types>] and headless_behavior: <"emit_in_output" | "skip" | "error"> in its registration metadata. The framework raises a registration error if gui_operations is non-empty without headless_behavior. In headless mode, the framework enforces the declared behavior automatically: emit_in_output places the URL/path in the JSON response, skip omits the operation with a warning, error exits 4.
Acceptance Criteria
- A command with
gui_operations: ["browser_open"]andheadless_behavior: "emit_in_output"returnsdata.open_urlin headless mode - A command with
gui_operations: ["browser_open"]but noheadless_behaviorraises a registration error - In non-headless mode, the GUI operation proceeds normally
meta.headless: trueis included in all responses when headless mode is active
Schema
Types: manifest-response.md · response-envelope.md
GUI-launching commands extend CommandEntry with:
| Field | Type | Required | Description |
|---|---|---|---|
gui_operations |
string[] | yes | Types of display operations performed (e.g. ["browser_open", "desktop_notification"]) |
headless_behavior |
"emit_in_output" | "skip" | "error" |
yes | How the framework handles each gui_operations type in headless mode |
ResponseMeta is extended with:
| Field | Type | Description |
|---|---|---|
headless |
boolean | true when the command ran in headless mode |
Wire Format
$ tool open --schema
{
"gui_operations": ["browser_open"],
"headless_behavior": "emit_in_output",
"parameters": {
"url": { "type": "string", "required": true, "description": "URL to open in the browser" }
},
"exit_codes": {
"0": { "name": "SUCCESS", "description": "URL opened or returned in output", "retryable": false, "side_effects": "complete" },
"4": { "name": "NO_TTY", "description": "GUI unavailable and headless_behavior=error", "retryable": false, "side_effects": "none" }
}
}
Headless response (headless_behavior: "emit_in_output"):
{
"ok": true,
"data": { "open_url": "https://example.com/dashboard" },
"error": null,
"warnings": [],
"meta": { "duration_ms": 5, "headless": true }
}
Example
register command "open":
gui_operations: [browser_open]
headless_behavior: emit_in_output
parameters:
url: type=string, required=true, description="URL to open"
register command "notify":
gui_operations: [desktop_notification]
headless_behavior: skip
register command "preview":
gui_operations: [browser_open]
headless_behavior: error
# register command "bad-open":
# gui_operations: [browser_open]
# (no headless_behavior)
# → framework error: gui_operations requires headless_behavior declaration
Related
| Requirement | Tier | Relationship |
|---|---|---|
| REQ-F-057 | F | Enforces: headless environment detection that triggers headless_behavior |
| REQ-C-023 | C | Specializes: editor invocation is one type of gui_operations with a declared headless fallback |
| REQ-C-015 | C | Composes: gui_operations and headless_behavior are part of --schema output |
| REQ-F-004 | F | Wraps: meta.headless and data.open_url are carried in ResponseEnvelope |