spec: elevate control plane decisions to top-level and expand defense… - #9
Conversation
… gates - Elevate authorization decisions (allow, deny, ask, defer) to top-level decision and reason, cleanly separating the control plane from the data plane. - Restrict hookSpecificOutput to surgical data plane mutations (updatedPrompt, updatedResponse, updatedOutput, updatedContent, updatedResponseBodyBase64), while retaining backward-compatible fallback for legacy handlers. - Upgrade SessionStart, AfterModelResponse, PostToolUse, PostNetworkAccess, and SubagentStart to standard Gate points based on enterprise defense requirements. - Add transport-adaptive execution clause for AfterModelResponse (batch/buffer can gate/transform; raw token streaming falls back to observe). - Add optional delegation_depth and delegation_chain to SubagentStart payload to defend against recursive subagent storms. - Relax non-interactive ask handling to support asynchronous turn suspension with resumption tokens. - Keep SessionRevoke (Gate 12) deferred for future spec versions. - Synchronize canonical and website JSON schemas, docs, and validation fixtures.
trend-kyle-huang
left a comment
There was a problem hiding this comment.
Overall I support the direction of separating the top-level control plane from event-specific data-plane mutations, and I agree with promoting these lifecycle boundaries to Gates where the host can retain control. The inline comments below are intended as guardrails and contract clarifications rather than requests to revert that design. One documentation follow-up: please also update the response field-reference table, which still says the root decision can only be block.
| and `permissionDecisionReason` inside `hookSpecificOutput` when top-level `decision` | ||
| is omitted. | ||
|
|
||
| | Core Gate | Canonical control response | Data plane rewrite support (`hookSpecificOutput`) | Effect | |
There was a problem hiding this comment.
I agree with making the top-level field the canonical control plane. Could we make the requirement and precedence explicit here? My understanding is that a new-style Gate response with control or mutation intent MUST carry a top-level decision; omission remains valid only for a no-control/metadata response or as a legacy compatibility path. When the top-level decision is present, the host should ignore legacy permissionDecision / nested decision.behavior for authorization. For the Post Gates, please also state that the controlled effect is delivery, rendering, or context ingestion—not rollback of the completed model, tool, or network operation. That would preserve the design while removing precedence and rollback ambiguity.
There was a problem hiding this comment.
Updated in 01997b4. Made the requirement and precedence explicit in spec/0.1/core.md:
- Any new-style Gate response intending authorization control or data plane mutation MUST carry a top-level
decision. - When present, the top-level decision is canonical, and hosts MUST ignore legacy nested decisions (
permissionDecision,decision.behavior). Omission is reserved for passive observation or legacy fallback. - For Post Gates (
AfterModelResponse,PostToolUse,PostNetworkAccess), explicitly clarified that the controlled effect is strictly delivery to caller, rendering to user, or ingestion into session context—not rollback or undoing of already completed model, tool, or network operations.
| permission decisions or supply `updatedResponse` in `hookSpecificOutput` to | ||
| sanitize model outputs (such as removing phishing URLs, PII, or prompt leaks) | ||
| before the response is rendered to users or appended to context. In raw | ||
| unbuffered streaming mode where tokens have already been emitted, this event |
There was a problem hiding this comment.
The buffered-Gate / raw-streaming-Observe distinction makes sense. The capability model currently requires one mode per event, though, so a host that supports both transports cannot accurately advertise a runtime-dependent mode. Could we scope a capability declaration to a specific host configuration/transport? A configuration that may expose tokens before hook evaluation MUST declare this event observe; it may declare gate only when all relevant output is retained until the decision resolves.
There was a problem hiding this comment.
Updated in 01997b4. Scoped AfterModelResponse capability declarations to the specific host configuration/transport:
- A configuration that may expose unbuffered tokens or partial output to users/agent context before hook evaluation MUST declare
observe. - A host MAY declare
gateonly for configurations where all output is retained and buffered until the hook decision resolves.
| declare `gate` when the native timing is post-effect, a response cannot be | ||
| enforced, redaction removes the security-relevant information needed for the | ||
| declared boundary, or the event registry classifies the event as Observe. | ||
| A host MAY also declare an optional `failure_mode` for each declared Gate |
There was a problem hiding this comment.
Non-blocking design suggestion: consider deferring closed and bounded_open to a separate enforcement profile/RFC. Core 0.1 still normatively requires fail-open for handler errors/timeouts and later says that no fail-closed mode is introduced. If these values stay here, they need precise behavior, applicable failure classes, precedence against the Core default, and definitions for when the open bound is exhausted; otherwise a declaration can claim a mode that Core does not define.
There was a problem hiding this comment.
Agreed. Removed closed and bounded_open from Core 0.1 capability declaration and documentation in 01997b4 to preserve strict normative fail-open consistency. Alternate failure enforcement profiles are now explicitly noted as deferred to future dedicated RFCs.
|
|
||
| For Gates supporting `ask`, `ask` MUST use the host approval flow. If the host is | ||
| non-interactive and does not support asynchronous turn suspension, it MUST treat | ||
| `ask` as `deny`. A host supporting asynchronous suspension MAY suspend the turn |
There was a problem hiding this comment.
I agree with allowing ask to represent pending authorization for both interactive and non-interactive hosts. Could Core keep the portable contract at the state transition: the operation MUST NOT execute until approval resolves, and a host without a native suspension mechanism MUST treat it as deny? Token format, issuance, expiry, replay protection, persistence, and resume APIs should be explicitly host/profile-specific; otherwise the example reads as though 0.1 defines an interoperable resumption-token protocol.
There was a problem hiding this comment.
Updated in 01997b4. The Core contract now strictly specifies the state transition:
- The operation MUST NOT execute or deliver until approval resolves.
- Non-interactive hosts without a suspension mechanism MUST treat
askasdeny. - Token format, issuance, TTL, replay protection, and resumption endpoints are explicitly marked as host-specific implementation mechanisms rather than a Core 0.1 wire standard.
| For `PostMemoryWrite`, hosts MUST ignore all response control fields. For | ||
| `PostNetworkAccess`, when declared as `gate` on a buffered transport, handlers | ||
| MAY provide control decisions (`allow`, `deny`) or replacement content | ||
| (`updatedResponseBodyBase64`) before delivering the body to the application caller. |
There was a problem hiding this comment.
I agree that PostNetworkAccess can Gate response delivery even though it cannot undo the request. The current event payload does not include the evaluated body or a recipient binding, however, so a handler cannot inspect/sanitize the content that updatedResponseBodyBase64 replaces. For this PR, could we either (a) keep delivery allow/deny based on the existing metadata and defer body rewriting, or (b) add the complete buffered body, consumer binding, valid-base64 rule, success-only applicability, and replacement/handler-ordering semantics described by RFC 0005? An output-only replacement should not be described as content inspection.
There was a problem hiding this comment.
Adopted Option (a) in 01997b4. Kept PostNetworkAccess as a Gate governing response delivery (allow delivers to caller, deny withholds from caller). Response body payload inspection and base64 rewriting are deferred to RFC 0005.
| "type": "object", | ||
| "additionalProperties": true | ||
| }, | ||
| "updatedContent": { |
There was a problem hiding this comment.
I support the expanded data-plane mutation surface, but the replacement shapes should match the values they replace. PreMemoryWrite.content accepts any JSON value, while updatedContent is string-only; similarly updatedResponse and updatedOutput should align with their event payload shapes, and updatedResponseBodyBase64 should validate the promised encoding if retained. Please also define the host behavior when a schema-valid replacement fails native validation—silently falling back to the original sensitive value would be unsafe.
There was a problem hiding this comment.
Updated in 01997b4:
- Changed
updatedContentto accept any JSON value ({}), aligning withPreMemoryWrite.content. - Aligned
updatedResponsewithAfterModelResponse.responseobject structure. - Removed
updatedResponseBodyBase64(deferred to RFC 0005). - Added a normative rule in Core: if a schema-valid data-plane rewrite fails native host validation, the host MUST fail closed (abort turn or treat as deny) and MUST NOT silently fall back to the unredacted original payload.
… boundaries, and schemas
|
Thank you @trend-kyle-huang for the thorough review and constructive guardrails! All feedback items have been addressed in commit
|
… gates
Summary
Describe the problem and resulting behavior. Link the related Issue, Discussion,
or existing RFC when applicable.
Website impact
List the pages, downloads, or directly rendered spec sources updated in this
PR. If no website change is needed, explain why.
Validation
Describe the checks performed and their results.
Checklist
npm run validateandnpm run build.