From 50435265922f9ad3fed8ce4d8c46b73ba0ed33b2 Mon Sep 17 00:00:00 2001 From: Mathis Blanc Date: Fri, 24 Jul 2026 09:10:48 +0200 Subject: [PATCH] Accept client-owned prompt metadata --- docs/features/codex-prompt-center.md | 2 +- schemas/codex-prompt-catalog-v1.schema.json | 7 +++++-- src/codex_prompt_center.mjs | 2 ++ test/codex_prompt_center.test.mjs | 14 ++++++++++++++ 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/docs/features/codex-prompt-center.md b/docs/features/codex-prompt-center.md index 73aa10d..a6f7ab1 100644 --- a/docs/features/codex-prompt-center.md +++ b/docs/features/codex-prompt-center.md @@ -51,7 +51,7 @@ The catalog is intentionally broader than the editor. Context Room discovers gro - **Editable here**: the runtime publishes an official baseline, an effective value, `editable: true`, `securityClass: local_user_editable`, an allowed editable runtime status, and one coherent override strategy. Only model-base, developer, local compaction, and selectable collaboration targets can enter this class. - **Configurable elsewhere**: another local Codex configuration or task-history surface owns the value. Prompt Center shows the owner and read-only reason instead of competing with it. -- **Protected**: security-, protocol-, enforcement-, lifecycle-, or platform-coupled instructions are metadata-only. Prompt text and hashes are not published to Context Room. +- **Protected**: security-, protocol-, enforcement-, lifecycle-, platform-, or client-owned instructions are metadata-only. Prompt text and hashes are not published to Context Room. - **Server-owned**: a remote service owns the instruction. The target is metadata-only and cannot be replaced locally. The platform-supplied system prompt and remote/server compaction instructions cannot be read or replaced through this local integration. Prompt Center shows only their metadata and read-only reason. This boundary is enforced by both the JSON catalog contract and Context Room's runtime checks; a forged `editable: true` flag is rejected. diff --git a/schemas/codex-prompt-catalog-v1.schema.json b/schemas/codex-prompt-catalog-v1.schema.json index 09f8331..3d8def5 100644 --- a/schemas/codex-prompt-catalog-v1.schema.json +++ b/schemas/codex-prompt-catalog-v1.schema.json @@ -213,6 +213,7 @@ "history_semantics", "executable_contract", "configurable_elsewhere", + "client_owned", "server_owned" ] } @@ -471,7 +472,8 @@ "lifecycle_managed", "history_semantics", "executable_contract", - "configurable_elsewhere" + "configurable_elsewhere", + "client_owned" ] } }, @@ -534,7 +536,8 @@ "lifecycle_managed", "history_semantics", "executable_contract", - "configurable_elsewhere" + "configurable_elsewhere", + "client_owned" ] } } diff --git a/src/codex_prompt_center.mjs b/src/codex_prompt_center.mjs index 3288dba..9f50029 100644 --- a/src/codex_prompt_center.mjs +++ b/src/codex_prompt_center.mjs @@ -228,6 +228,7 @@ const CATALOG_SECURITY_CLASSES = new Set([ "history_semantics", "executable_contract", "configurable_elsewhere", + "client_owned", "server_owned", ]); const CATALOG_OVERRIDE_CONFLICT_CODES = new Set([ @@ -267,6 +268,7 @@ const PROTECTED_SECURITY_CLASSES = new Set([ "history_semantics", "executable_contract", "configurable_elsewhere", + "client_owned", ]); function promptCenterError(message, statusCode = 400, code = "codex_prompt_request_failed", details = undefined) { const error = new Error(message); diff --git a/test/codex_prompt_center.test.mjs b/test/codex_prompt_center.test.mjs index 5b93179..b4ed65c 100644 --- a/test/codex_prompt_center.test.mjs +++ b/test/codex_prompt_center.test.mjs @@ -440,6 +440,7 @@ test("durable JSON schemas match the strict Rust catalog, manifest, publication "history_semantics", "executable_contract", "configurable_elsewhere", + "client_owned", "server_owned", ]); assert.deepEqual(catalogSchema.$defs.overrideConflict.properties.code.enum, [ @@ -1008,6 +1009,19 @@ test("catalog authority classes, effective content, and editable strategy fail c assert.equal(validProtected.official, null); assert.equal(validProtected.effective, null); + const clientOwnedCatalog = fixtureCatalog(); + const clientOwnedTarget = clientOwnedCatalog.groups[1].targets[0]; + clientOwnedTarget.id = "protected/client/tui_init"; + clientOwnedTarget.label = "TUI /init command prompt"; + clientOwnedTarget.securityClass = "client_owned"; + const clientOwned = createCodexPromptCenterProvider({ + storageRoot: makeStorage(t), + catalog: clientOwnedCatalog, + }).readTarget(clientOwnedTarget.id); + assert.equal(clientOwned.kind, "protected"); + assert.equal(clientOwned.editable, false); + assert.equal(clientOwned.securityClass, "client_owned"); + const serverOwnedCatalog = fixtureCatalog(); serverOwnedCatalog.groups[1].targets[0] = strictCatalogTarget({ id: "synthetic/server/owned",