Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion docs/spec/instrument/hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ Schema: [`hooks/agent-trigger.json`](https://genai-security-project.github.io/ag

Fires when the agent is activated by an external triggering condition (event arrival, scheduled tick, A2A inbound message, user-initiated session, or system-issued activation). For A2A-mediated delegation, `trigger_type: "a2a_inbound"` carries the originating peer identity; for in-process subagent spawns, see [`subagentStart`](#subagentstart).

**Payload:** `trigger_type` (`user_message`, `scheduled`, `external_event`, `a2a_inbound`, `system`), `trigger_source` (shape depends on `trigger_type`), optional `intent` (IBAC adopters populate; the Intent registered at sessionStart, carried here for activation-time policy checks).
**Payload:** `trigger_type` (`user_message`, `scheduled`, `external_event`, `a2a_inbound`, `system`), `trigger_source` (shape depends on `trigger_type`), optional `intent` (IBAC adopters populate, for activation-time policy checks). [Specification §8.4](./specification.md#84-intent) defines what establishes the canonical session Intent and when a later Intent is rejected.

**Decision:** ALLOW / DENY / MODIFY. Guardian MAY rewrite the trigger payload (e.g. redact PII) before activation.

Expand Down
2 changes: 1 addition & 1 deletion docs/spec/instrument/specification.md
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ Schema: [`provenance-summary.json`](https://genai-security-project.github.io/age

Intent is OPTIONAL and is defined in [Concepts › Intent](../../concepts/intent.md) (normative). Wire fields: `raw`, `parsed` (capability list), `parser_provenance` (REQUIRED if `parsed` present; `origin` MUST be `user_input`), `scope_mode`.

**Intent immutability enforcement (normative).** The invariant is defined in [Concepts › Intent](../../concepts/intent.md): once established (via `sessionStart` or the first `agentTrigger` for the session), `Intent.parsed` is fixed and may grow only through an approver's `intent_extension` via the ASK flow (§9.1). The framework MUST enforce it: any attempt to modify `Intent.parsed` by the runtime LLM, by tool outputs, or by data crossing an `untrusted` channel MUST be ignored or rejected, and SHOULD be recorded as an audit event. This rule is load-bearing for IBAC's central security claim: the capability set is fixed before untrusted data enters and can grow only through explicit, audited approver action.
**Intent immutability enforcement (normative).** The invariant is defined in [Concepts › Intent](../../concepts/intent.md): once established (via `sessionStart` when it carries `intent`, or otherwise via the first `agentTrigger` for the session that carries `intent`, before any content-bearing step), `Intent.parsed` is fixed as the canonical session Intent and may grow only through an approver's `intent_extension` via the ASK flow (§9.1). Any subsequent `agentTrigger` that proposes a different `intent` MUST be rejected, unless the difference is capabilities an approver already added through an `intent_extension` with `scope: session`. The framework MUST enforce it: any attempt to modify `Intent.parsed` by the runtime LLM, by tool outputs, or by data crossing an `untrusted` channel MUST be ignored or rejected, and SHOULD be recorded as an audit event. This rule is load-bearing for IBAC's central security claim: the capability set is fixed before untrusted data enters and can grow only through explicit, audited approver action.

### 8.5 Size-based archival (optional)

Expand Down
2 changes: 1 addition & 1 deletion specification/v0.1.0/hooks/agent-trigger.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
},
"intent": {
"type": "object",
"description": "Optional Intent registered at session start (IBAC adopters populate)",
"description": "Optional Intent for the session (IBAC adopters populate). Specification §8.4 defines what establishes the canonical session Intent and when a later Intent is rejected.",
"properties": {
"raw": { "type": "string" },
"parsed": {
Expand Down
2 changes: 1 addition & 1 deletion specification/v0.1.0/hooks/session-start.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
},
"intent": {
"type": "object",
"description": "Optional Intent established at session start. Same shape as Intent inside agentTrigger. When present, the Guardian MUST treat this as the canonical session Intent: any later agentTrigger that attempts to establish a different Intent MUST be rejected, and Intent.parsed MUST NOT be mutable except via the intent_extension mechanism in ask-details.json.",
"description": "Optional Intent established at session start. Same shape as Intent inside agentTrigger. When present, the Guardian MUST treat this as the canonical session Intent: any later agentTrigger that attempts to establish a different Intent MUST be rejected, and Intent.parsed MUST NOT be mutable except via the intent_extension mechanism in ask-details.json. When sessionStart carries no Intent, Specification §8.4 defines what establishes the canonical session Intent.",
"properties": {
"raw": { "type": "string" },
"parsed": {
Expand Down
12 changes: 12 additions & 0 deletions tests/test_hook_taxonomy.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,3 +352,15 @@ def test_agbom_update_triggers_cover_every_component_type():
f"AgBOM update triggers omit component type {comp_type!r}"
)


def test_canonical_intent_rule_lives_in_section_8_4():
"""Issue #149: section 8.4 states the rule, and the other places point to it."""
for name in ("session-start.json", "agent-trigger.json"):
schema = json.loads((HOOK_SCHEMAS / name).read_text(encoding="utf-8"))
assert "§8.4" in schema["properties"]["intent"]["description"]
hooks = (ROOT / "docs/spec/instrument/hooks.md").read_text(encoding="utf-8")
assert "#84-intent" in hooks.split("## agentTrigger")[1].split("\n---")[0]
spec = (ROOT / "docs/spec/instrument/specification.md").read_text(encoding="utf-8")
section = spec.split("### 8.4 Intent")[1].split("### 8.5")[0]
for phrase in ("before any content-bearing step", "MUST be rejected, unless", "`scope: session`"):
assert phrase in section
Loading