From 59bed48d71d182ce746d10067feafb54e609255f Mon Sep 17 00:00:00 2001 From: Michael Heller <21163552+mdheller@users.noreply.github.com> Date: Tue, 4 Aug 2026 15:16:43 -0400 Subject: [PATCH] docs(capability-schema): mark as not runtime-enforced (register #9) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 2026-07-29 estate audit register item #9: `requires_user_consent` in `schemas/sourceos-capability.schema.json` has no reader — the one example fixture only ever sets it to `false`, so the true-case is never exercised even in a fixture, let alone enforced. Verified directly (2026-08-04), and the gap is broader than the original item named: EVERY declarative field in this schema is unread — `default_decision` (allow/deny/prompt/audit), `requires_user_consent`, `audit`, `privacy_impact`, `compatible_authority_domains` alike. The only two consumers (`tools/validate_json_schemas.py`, `tools/validate_control_plane_ examples.py`) check shape and `schema_version` only. This daemon's actual capability-gating code, `src/sourceos_syncd/ orchestration_events.py`, uses a completely different record shape (`capability_id`/`effect_class`/`required_policy_outcome`/ `approval_mode`) with zero connection to this schema. Did NOT wire a reader here. Forcing a unification between the two capability models without knowing which one is meant to win would be exactly the "declared enforcement that isn't" pattern the register exists to catch — orchestration_events.py's gate already works; grafting requires_user_consent onto it by guessing at intent risks either a fake pass (the field gets read but nothing branches on it, same disease) or breaking a working gate. What this PR does instead: makes the disconnect impossible to miss. A `$comment` on the schema and a README callout both state, plainly, that this file is a declarative capability catalog with no runtime consumer, name the real gate, and pose the actual open question — unify into orchestration_events.py, or is this a forward-declared catalog that predates the gate it was meant to feed? That's a design decision for a human, not an implementation guess. Verified: schema still valid JSON, both existing validators (validate_json_schemas.py, validate_control_plane_examples.py) still pass unchanged. --- README.md | 2 +- schemas/sourceos-capability.schema.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index aabd3f4..184a4fa 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Control plane: - [`schemas/sourceos.process-provenance.v0.1.schema.json`](schemas/sourceos.process-provenance.v0.1.schema.json) - [`schemas/sourceos-event.schema.json`](schemas/sourceos-event.schema.json) - [`schemas/sourceos-service.schema.json`](schemas/sourceos-service.schema.json) -- [`schemas/sourceos-capability.schema.json`](schemas/sourceos-capability.schema.json) +- [`schemas/sourceos-capability.schema.json`](schemas/sourceos-capability.schema.json) — **not runtime-enforced.** Shape-validated only (`tools/validate_json_schemas.py`, `tools/validate_control_plane_examples.py`); `default_decision`/`requires_user_consent`/`audit`/`privacy_impact` are declarative fields nothing in this daemon reads. Real capability gating lives in `src/sourceos_syncd/orchestration_events.py`'s `capability_id`/`effect_class`/`required_policy_outcome`/`approval_mode` model, which this schema does not connect to. - [`schemas/sourceos-launch-manifest.schema.json`](schemas/sourceos-launch-manifest.schema.json) - [`schemas/sourceos-incident.schema.json`](schemas/sourceos-incident.schema.json) diff --git a/schemas/sourceos-capability.schema.json b/schemas/sourceos-capability.schema.json index 4931f3d..c8fddc6 100644 --- a/schemas/sourceos-capability.schema.json +++ b/schemas/sourceos-capability.schema.json @@ -2,6 +2,7 @@ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://sourceos.dev/schemas/sourceos-capability.schema.json", "title": "SourceOS Capability Definition", + "$comment": "NOT WIRED TO RUNTIME (verified 2026-08-04). `default_decision`, `requires_user_consent`, `audit`, and `privacy_impact` are declarative only: nothing in this daemon reads them to gate, prompt, or refuse anything. The two consumers that touch this file (tools/validate_json_schemas.py, tools/validate_control_plane_examples.py) check shape/schema_version only. The daemon's REAL capability-gating code is src/sourceos_syncd/orchestration_events.py, which uses a different record shape entirely (capability_id/effect_class/required_policy_outcome/approval_mode) with no connection to this schema. Before adding a fixture with requires_user_consent:true or wiring a reader here, decide: should this schema be unified into orchestration_events.py's real gate, or is it a forward-declared capability catalog that predates the gate it was meant to feed? (declared-unenforced register item #9, 2026-07-29 estate audit)", "type": "object", "required": [ "schema_version",