# Observation Rules Schema — Layer 3 Vocabulary Schema # KNO Schema Version: 0.1.0 # # Layer 3 vocabulary schema for the F3 observation tap. Each # `content/observation-rules/*.kno` instance defines ONE rule that # converts a matching AS2 activity into an observation entity. The tap # (services/pspace-api/src/lib/observations.ts) loads every rule at # startup and evaluates each in turn against incoming activities; # matches produce observations. # # DOMAIN: Tap configuration vocabulary # PURPOSE: Make the activity → observation mapping .kno-driven, not # hardcoded. Adding a new observation class = adding a .kno # file. Removing a rule = deleting a .kno file. No code change. # # DESIGN DECISIONS: # - DD-OR-01: Each rule is its own .kno file. Open-enum filesystem # pattern (matches perception-domain-schema, F1) — adding a rule is # adding a file, removing a rule is deleting a file. No closed-enum # tax on rule additions. # - DD-OR-02: Match criteria use AS2 fields plus Possibility's # activity object/target conventions. `activity_type` is the AS2 # verb (Create / Update / Delete / Decide / etc.); `entity_type` # filters by the activity's `object.type` (e.g. "schema", # "decision"); `entity_path_prefix` filters by `object.id` prefix # for entity-storage routing. # - DD-OR-03: Wildcards use `*` (match-anything). Empty/omitted # match criterion = "don't filter on this dimension". This keeps # rules concise — most rules filter on 1-2 dimensions. # - DD-OR-04: Rule emits a `routing.domains[]` array of # perception-domain slugs (NOT agent IDs — B-004). Drift-guard # enforces that every rule's domains[] resolve to existing # perception-domain entries. # - DD-OR-05: Default behaviour for missing-data activities (AS2 # does not require entity_type): rule MAY declare a # `match_when_missing` field. If true, the rule fires when the # activity lacks the required match field. If false (default), it # does not. Per #2018 carry-forward A-5 default rule. # - DD-OR-06: A `disabled: true` flag lets rules be retired without # deleting them — useful for short-term incident response (mute # a noisy rule fast). Drift-guard surfaces disabled rules so they # don't sit muted forever. # # Created for: M22-P2 F3 (#851). # ============================================================================= # SCHEMA DECLARATION # ============================================================================= $schema: kno@0.0.9 # ============================================================================= # BASIC TIER # ============================================================================= id: 01KQ43ZVJYSNKM0RY794EA31DP slug: observation-rule-schema type: spec version: 0.1.0 # ============================================================================= # STANDARD TIER # ============================================================================= title: "Observation Rules Schema" purpose: | Define the schema for observation-rule entities — the .kno-driven configuration that determines which AS2 activities the F3 tap converts into observations, and how those observations are shaped and routed. **Rule lifecycle:** 1. Author creates `content/observation-rules/.kno` declaring: - When to match (activity_type, entity_type, ...) - What observation_type to produce - What severity / data shape to assign - Which perception-domains receive the resulting observation 2. Tap loads all rules at startup (or on .kno change) 3. Every activity passes through every enabled rule 4. Matching rules produce observations with the rule's stamp **Why per-rule .kno files (not a single rules.kno)?** Each rule is independently maintained. Authors of a new rule can add a single file without touching others. Drift-guard tests can validate rules independently. Rules can be enabled/disabled per-file. Matches the perception-domain pattern from F1 (one term per file). **Layer 3 position:** Layer 3 vocabulary. Pairs with `observation-schema.kno` (the produced-entity shape). # ============================================================================= # RICH TIER — Provenance & Taxonomy # ============================================================================= provenance: origin: id: 01KQ43ZVJYSNKM0RY794EA31DP timestamp: "2026-04-25T23:15:00Z" tool: manual issue: "https://github.com/PossibilityTruthy/possibility-space/issues/851" taxonomy: topics: - observations - tap - rules - vocabulary - m22 keywords: - observation-rule - activity-tap - perception-routing # ============================================================================= # RICH TIER — Relationships # ============================================================================= relationships: related_to: - xri: "kno://specs/observation-schema" reason: "Each rule produces observations conforming to this schema" - xri: "kno://specs/activity-schema" reason: "Rules match against AS2 activity fields" - xri: "kno://specs/perception-domain-schema" reason: "Each rule's routing.domains[] are slugs of perception-domain entities" quality: completeness: 0.9 last_reviewed: "2026-04-25" review_status: draft # ============================================================================= # HISTORY # ============================================================================= _history: version: 1 created: "2026-04-25T23:15:00Z" created_by: "claude" modified: "2026-04-25T23:15:00Z" modified_by: "claude" # ============================================================================= # SPECIFICATION # ============================================================================= spec: status: Draft changelog: - version: "0.1.0" date: "2026-04-25" changes: - "Initial creation for M22-P2 F3 (#851)" - "Per-rule .kno files (DD-OR-01) — open vocabulary" - "Capability-based routing.domains[] (DD-OR-04, B-004 mitigation)" - "match_when_missing flag (DD-OR-05) for AS2's optional entity_type" schema: type: object required: - id - slug - type - version - match - emit properties: $schema: type: string const: "observation-rule@0.1.0" id: type: string slug: type: string description: | Kebab-case identifier. MUST equal the filename (without `.kno`) so the drift-guard can resolve `produced_by_rule` references. type: type: string const: "observation-rule" version: type: string name: type: string description: "Human-readable name" description: type: string description: "What this rule produces and why" disabled: type: boolean default: false description: | When true, the rule is loaded but not evaluated. Used for temporary muting (incident response) without deleting the file. Drift-guard surfaces disabled rules so they don't sit muted indefinitely. (DD-OR-06) # ----------------------------------------------------------------------- # MATCH CRITERIA (DD-OR-02, DD-OR-03) # ----------------------------------------------------------------------- match: type: object description: | Match criteria. Empty/omitted criterion = "don't filter on this dimension". Use "*" wildcard explicitly when "match everything" needs to be visible to readers. properties: activity_type: type: string description: | AS2 activity type to match (Create / Update / Delete / Add / Remove / Decide / etc.). "*" matches any. examples: - "Create" - "Decide" - "*" entity_type: type: string description: | Filter by activity.object.type. "*" matches any. Common values: - schema (specs/*.kno entities) - decision (content/decisions/*.kno) - capability (content/capabilities/*.kno) - corpus (analysis corpora) - user (user entities) examples: - "schema" - "decision" entity_path_prefix: type: string description: | Filter by activity.object.id prefix. Useful when entity_type isn't sufficient (e.g. "spec entities under specs/auth/" vs all spec entities). examples: - "specs/auth/" match_when_missing: type: boolean default: false description: | When true, the rule fires even if a match field is absent from the activity. Used as a default rule for activities lacking entity_type (which AS2 does not require). (DD-OR-05) # ----------------------------------------------------------------------- # EMIT SHAPE # ----------------------------------------------------------------------- emit: type: object required: - observation_type - severity - routing description: | Specification of the observation entity to produce. The tap combines this with the source activity to construct the observation. properties: observation_type: type: string description: | Free-form slug that becomes the produced observation's `observation_type` field. SHOULD be kebab-case. Multiple rules may produce the same observation_type with different data shapes. examples: - "decision-recorded" - "schema-change" - "entity-lifecycle" severity: type: string enum: - info - notice - warning - attention default: info routing: type: object required: - domains description: "Capability-based routing (DD-OR-04)" properties: domains: type: array items: type: string description: | Perception-domain slugs that should receive matching observations. Drift-guard asserts every entry resolves to a content/perception-domains/.kno file. examples: - ["schemas"] - ["entity-health", "drift-surfaces"] recommended_priority: type: string enum: - low - normal - high data_template: type: object description: | Optional template for the observation's data field. The tap may carry through specific activity fields verbatim (e.g. activity.object.xri → data.entity_xri). Free-form — each rule documents its own data shape via this template. # ============================================================================= # EXAMPLES # ============================================================================= examples: - title: "Decision-recorded rule" description: "Fires on every Decide activity" content: | $schema: observation-rule@0.1.0 id: 01KQ43ZVJYDEMOXXX1RULEEXAMPLE slug: decision-recorded type: observation-rule version: 0.1.0 name: "Decision Recorded" description: "Emits an observation each time a Decide AS2 activity is logged." match: activity_type: Decide emit: observation_type: decision-recorded severity: info routing: domains: - acceptance-criteria-history # A12 Hindsight Reviewer recommended_priority: normal