From b893c2cdbd496374498c37bd623603ae8ced7801 Mon Sep 17 00:00:00 2001 From: kl3inIT Date: Sun, 26 Jul 2026 13:39:15 +0700 Subject: [PATCH 1/5] docs: record engineering migration backlog --- docs/roadmap.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/roadmap.md b/docs/roadmap.md index f607951f..18000e58 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -107,6 +107,21 @@ belongs in one active increment. one realistic task, explicit data-retention policy, and rollback plan. Expand to 20-100 users only after this gate passes. +## Engineering Backlog + +- Complete the API-facing exception migration: inventory legacy validators and + services that still expose `IllegalArgumentException` or require dedicated + transport handlers, move intended business failures to the shared + `BusinessException` categories and stable RFC 9457 codes, add contract tests, + then remove the compatibility translator only after no caller depends on it. +- Refactor oversized Spring Modulith packages into cohesive internal + subpackages while preserving each logical module and its public named + interface. Choose package boundaries from actual responsibilities + (application use cases, domain model, inbound API, outbound infrastructure), + document allowed dependencies, and keep Modulith verification in CI; do not + create one Gradle module or one top-level Modulith module per class or asset + profile. + ## Later, Only With Evidence Screenpipe capture, controlled SOP effectivity, Skill installation, executable From f648c567515aa1dd0e5831a7016a296866fd8d52 Mon Sep 17 00:00:00 2001 From: kl3inIT Date: Sun, 26 Jul 2026 15:44:16 +0700 Subject: [PATCH 2/5] feat(asset-registry): complete golden POC and MCP onboarding --- .github/workflows/ci.yml | 3 + ARCHITECTURE.md | 39 +- .../AssetRegistryIntegrationTests.java | 316 ++++++++++++++++ .../src/test/resources/db/test-foundation.sql | 24 ++ contracts/openapi.json | 2 +- .../AssetRegistryCoordinator.java | 25 ++ .../core/assetregistry/AssetView.java | 8 + demo/fixtures/asset-registry/README.md | 28 ++ .../capability-pack-template.json | 36 ++ .../fixtures/asset-registry/mock-tickets.json | 10 + .../asset-registry/prompt-template.json | 110 ++++++ .../asset-registry/quality-checklist.json | 12 + .../asset-registry/success-metrics.json | 13 + .../support-sla-and-escalation.md | 24 ++ .../asset-registry/work-instruction.json | 56 +++ docs/increments/active/README.md | 6 - .../design.md | 8 +- .../gate-decisions.md | 24 +- .../plan.md | 60 +-- .../ui-reference-audit.md | 0 .../verification.md | 92 +++++ docs/increments/completed/README.md | 5 + docs/roadmap.md | 17 +- docs/runbooks/mcp-asset-delivery.md | 43 ++- docs/specs/domains/asset-registry.md | 19 + docs/tests/domains/asset-registry.md | 7 + docs/vision.md | 19 +- .../scripts/configure-keycloak-mcp.sh | 208 +++++++++++ infrastructure/deployment/scripts/deploy.sh | 3 + .../deployment/scripts/smoke-production.sh | 60 +++ .../scripts/test-keycloak-mcp-onboarding.sh | 200 ++++++++++ .../scripts/test-web-forwarded-port.sh | 12 + infrastructure/keycloak/Dockerfile | 2 + .../keycloak/mcp-basic-client-scope.json | 35 ++ .../keycloak/mcp-client-policies.json | 45 +++ .../keycloak/mcp-client-profiles.json | 37 ++ .../keycloak/mcp-dcr-registration-policy.json | 31 ++ web/nginx.conf | 12 + web/src/components/app-shell/app-sidebar.tsx | 3 +- .../assets/components/asset-detail-page.tsx | 9 + .../mcp/components/mcp-connect-page.tsx | 282 ++++++++++++++ web/src/routeTree.gen.ts | 21 ++ web/src/routes/_authenticated/connect.tsx | 8 + .../e2e/asset-registry-golden-poc.spec.ts | 345 ++++++++++++++++++ web/test/e2e/mcp-connect.spec.ts | 54 +++ 45 files changed, 2297 insertions(+), 76 deletions(-) create mode 100644 demo/fixtures/asset-registry/README.md create mode 100644 demo/fixtures/asset-registry/capability-pack-template.json create mode 100644 demo/fixtures/asset-registry/mock-tickets.json create mode 100644 demo/fixtures/asset-registry/prompt-template.json create mode 100644 demo/fixtures/asset-registry/quality-checklist.json create mode 100644 demo/fixtures/asset-registry/success-metrics.json create mode 100644 demo/fixtures/asset-registry/support-sla-and-escalation.md create mode 100644 demo/fixtures/asset-registry/work-instruction.json rename docs/increments/{active => completed}/2026-07-25-unified-asset-registry-definition/design.md (98%) rename docs/increments/{active => completed}/2026-07-25-unified-asset-registry-definition/gate-decisions.md (85%) rename docs/increments/{active => completed}/2026-07-25-unified-asset-registry-definition/plan.md (88%) rename docs/increments/{active => completed}/2026-07-25-unified-asset-registry-definition/ui-reference-audit.md (100%) create mode 100644 docs/increments/completed/2026-07-25-unified-asset-registry-definition/verification.md create mode 100755 infrastructure/deployment/scripts/configure-keycloak-mcp.sh create mode 100755 infrastructure/deployment/scripts/test-keycloak-mcp-onboarding.sh create mode 100644 infrastructure/keycloak/mcp-basic-client-scope.json create mode 100644 infrastructure/keycloak/mcp-client-policies.json create mode 100644 infrastructure/keycloak/mcp-client-profiles.json create mode 100644 infrastructure/keycloak/mcp-dcr-registration-policy.json create mode 100644 web/src/features/mcp/components/mcp-connect-page.tsx create mode 100644 web/src/routes/_authenticated/connect.tsx create mode 100644 web/test/e2e/asset-registry-golden-poc.spec.ts create mode 100644 web/test/e2e/mcp-connect.spec.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 938735ba..53b0ab9d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -393,6 +393,9 @@ jobs: - name: Test web forwarded-port handling run: infrastructure/deployment/scripts/test-web-forwarded-port.sh + - name: Test Keycloak MCP client onboarding + run: infrastructure/deployment/scripts/test-keycloak-mcp-onboarding.sh + gate: name: CI Gate if: always() diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 66a7555f..da2fd026 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -9,7 +9,7 @@ This document records behavior and structure that exist in the repository on ```mermaid flowchart LR WEB[web React SPA] --> API[apps/api] - MCP[apps/mcp] --> CORE[core] + MCP[apps/mcp] -->|exchanged actor token| API API --> CORE WORKER[apps/worker] --> CORE CORE --> PG[(PostgreSQL 18
pgvector + AGE)] @@ -39,8 +39,8 @@ framework-neutral graph core), and never `core -> apps/integrations`. ## Current Runtime Responsibilities -- `core`: organization, permission, assistant, AI, and knowledge domain packages; - JPA repositories; application services; Flyway migrations. +- `core`: organization, permission, assistant, AI, knowledge, and Asset Registry + domain packages; JPA repositories; application services; Flyway migrations. - `apps/api`: REST endpoints, OIDC bearer-token boundary, server-derived actor, optional Spring AI normalization/chat, OpenAPI, health, and an `/api/admin/**` administration surface over the identity ledger and the source connections, @@ -54,13 +54,16 @@ framework-neutral graph core), and never `core -> apps/integrations`. and what it authenticates with come from the ledger on every poll, so an administrator's change takes effect on the next one without a restart. - `apps/mcp`: a stateless, bearer-authenticated Spring AI MCP server. Its - read-only `search_knowledge` tool forwards the caller token to the API search - contract, so agents use the same GraphRAG, OpenFGA, canonical ACL recheck, and - audit path as the product Assistant without owning database migrations or a - second retrieval implementation. + read-only Knowledge and Asset tools, Asset resources, and released Prompt + adapter exchange the inbound resource token for a short-lived actor token + scoped to canonical API contracts. Agents therefore use the same GraphRAG, + OpenFGA, live object authorization, and audit paths as the product without + bearer passthrough, repositories, database migrations, or a second delivery + implementation. - `web`: a Vite SPA with TanStack Router file routes, an authenticated shadcn - sidebar shell, generated Hey API clients for ordinary REST contracts, and an - AI Elements assistant workspace. The protected route layout owns session + sidebar shell, generated Hey API clients for ordinary REST contracts, an AI + Elements assistant workspace, and generic Asset catalog, detail/use, Pack + journey, governance, and MCP connection surfaces. The protected route layout owns session restoration and passes the verified identity into the shell; feature code does not repeat authentication gates. A separate `/admin` area reuses the same shell with a Permissions sidebar and is hidden from non-administrators by the @@ -71,6 +74,24 @@ database jobs carry ingestion work across processes. A specific Knowledge Asset publication outbox records direct-upload authorization projection attempts and the pinned OpenFGA model; no generic event framework has been introduced. +## Governed Asset Registry + +The side-by-side `core.assetregistry` module owns stable Asset identity, +accountable roles, mutable drafts, immutable digest-pinned revisions, distinct +review decisions, immutable releases, append-only availability history, and +actor-scoped consumption evidence. Prompt Template, Work Instruction, and +Capability Pack are code-owned profiles over this common kernel. Existing +Knowledge remains in its canonical ledger and is federated by exact visible +version; it is not copied into registry tables. + +All REST, Assistant, web, and MCP consumption resolves an exact released +version and live actor authorization. Capability Packs pin exact component +releases and independently authorize every item, so a replacement cannot +silently mutate an assigned journey and a denied component collapses to an +opaque access gap. Ownership health is derived from active owner and backup +owner assignments and exposes explicit orphaned/continuity-risk states without +changing the immutable release. + ## Persisted Model The identity ledger persists organizations, departments, users, and external diff --git a/apps/api/src/test/java/com/orgmemory/api/assetregistry/AssetRegistryIntegrationTests.java b/apps/api/src/test/java/com/orgmemory/api/assetregistry/AssetRegistryIntegrationTests.java index 8c3b1367..d0a2f219 100644 --- a/apps/api/src/test/java/com/orgmemory/api/assetregistry/AssetRegistryIntegrationTests.java +++ b/apps/api/src/test/java/com/orgmemory/api/assetregistry/AssetRegistryIntegrationTests.java @@ -46,7 +46,13 @@ import com.orgmemory.core.authorization.RelationshipTupleWriteResult; import com.orgmemory.core.authorization.ResourceRef; import com.orgmemory.core.knowledge.QueryEmbeddingPort; +import com.orgmemory.core.knowledge.PermissionAwareKnowledgeSearch; +import com.orgmemory.core.knowledge.RetrievedKnowledgeEvidence; +import com.orgmemory.core.knowledge.SecureKnowledgeSearchResult; import com.orgmemory.core.organization.CurrentActor; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.List; import java.util.Map; import java.util.UUID; @@ -67,6 +73,8 @@ import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.postgresql.PostgreSQLContainer; import reactor.core.publisher.Flux; +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.ObjectMapper; @SpringBootTest @Testcontainers @@ -82,6 +90,10 @@ class AssetRegistryIntegrationTests { UUID.fromString("44444444-4444-4444-4444-444444444444"); private static final UUID REVIEWER_ID = UUID.fromString("55555555-5555-5555-5555-555555555555"); + private static final UUID SUPPORT_AGENT_ID = + UUID.fromString("66666666-6666-6666-6666-666666666666"); + private static final UUID BACKUP_OWNER_ID = + UUID.fromString("77777777-7777-7777-7777-777777777777"); private static final UUID SPACE_ID = UUID.fromString("88888888-8888-4888-8888-888888888802"); private static final String MODEL_ID = "asset-model-1"; @@ -100,6 +112,13 @@ class AssetRegistryIntegrationTests { DEPARTMENT_ID, "Minh Tran", "minh@example.test"); + private static final CurrentActor SUPPORT_AGENT = new CurrentActor( + SUPPORT_AGENT_ID, + ORGANIZATION_ID, + UUID.fromString("33333333-3333-3333-3333-333333333333"), + "An Pham", + "an@example.test"); + private static final ObjectMapper JSON = new ObjectMapper(); @Container @ServiceConnection @@ -141,6 +160,9 @@ class AssetRegistryIntegrationTests { @MockitoBean QueryEmbeddingPort queryEmbeddings; + @MockitoBean + PermissionAwareKnowledgeSearch knowledgeSearch; + @MockitoBean ChatModelPort chat; @@ -157,6 +179,9 @@ void prepare() { eq(PROMPT_ROUTE), any(ChatGenerationRequest.class))) .thenReturn(Flux.just("{\"category\":\"access\"}")); + when(knowledgeSearch.search(any(), any(), any(), any())) + .thenReturn(new SecureKnowledgeSearchResult( + "asset-registry-empty-grounding", List.of())); } @TestConfiguration(proxyBeanMethods = false) @@ -827,6 +852,244 @@ void payloadReferencesCannotPointAcrossTenantBoundaries() { "inline://payload")); } + @Test + void goldenPocTransfersAReleasedSupportCapabilityToASecondUser() + throws IOException { + List tickets = JSON.readValue( + goldenFixture("mock-tickets.json"), + new TypeReference<>() { + }); + assertEquals(8, tickets.size()); + assertTrue(tickets.stream().allMatch(MockTicket::rubricPass)); + + when(knowledgeSearch.search(any(), any(), any(), any())) + .thenAnswer(invocation -> new SecureKnowledgeSearchResult( + invocation.getArgument(3) == null + ? "golden-grounding" + : invocation.getArgument(3), + List.of(goldenKnowledgeEvidence()))); + when(chat.stream( + eq(AiWorkload.PROMPT_EXECUTION), + eq(PROMPT_ROUTE), + any(ChatGenerationRequest.class))) + .thenAnswer(invocation -> { + ChatGenerationRequest request = invocation.getArgument(2); + MockTicket ticket = tickets.stream() + .filter(candidate -> + request.userPrompt().contains(candidate.id())) + .findFirst() + .orElseThrow(); + return Flux.just(JSON.writeValueAsString(Map.of( + "category", ticket.category(), + "slaTier", ticket.slaTier(), + "escalate", ticket.escalate(), + "response", "Use approved policy and cite support.sla-and-escalation@1"))); + }); + + AssetView prompt = createApprovedRelease( + AssetType.PROMPT_TEMPLATE, + "triage-customer-ticket", + goldenFixture("prompt-template.json"), + "1.0.0"); + AssetView instruction = createApprovedRelease( + AssetType.WORK_INSTRUCTION, + "classify-and-respond", + goldenFixture("work-instruction.json"), + "1.0.0"); + AssetView.Release promptRelease = prompt.releases().getFirst(); + AssetView.Release instructionRelease = + instruction.releases().getFirst(); + String packPayload = goldenFixture("capability-pack-template.json") + .replace("${WORK_INSTRUCTION_ASSET_ID}", instruction.id().toString()) + .replace("${WORK_INSTRUCTION_RELEASE_ID}", instructionRelease.id().toString()) + .replace("${PROMPT_ASSET_ID}", prompt.id().toString()) + .replace("${PROMPT_RELEASE_ID}", promptRelease.id().toString()); + AssetView pack = createApprovedRelease( + AssetType.CAPABILITY_PACK, + "l1-onboarding", + packPayload, + "1.0.0"); + AssetView.Release packRelease = pack.releases().getFirst(); + + assertTrue(pack.ownershipHealth().ownerPresent()); + assertFalse(pack.ownershipHealth().backupOwnerPresent()); + assertTrue(pack.ownershipHealth().continuityAtRisk()); + assets.assignRole( + AUTHOR, + pack.id(), + "user", + SUPPORT_AGENT_ID.toString(), + AssetRole.OWNER); + AssetView handedOver = assets.assignRole( + AUTHOR, + pack.id(), + "user", + BACKUP_OWNER_ID.toString(), + AssetRole.BACKUP_OWNER); + assertTrue(handedOver.ownershipHealth().ownerPresent()); + assertTrue(handedOver.ownershipHealth().backupOwnerPresent()); + assertFalse(handedOver.ownershipHealth().orphaned()); + assertFalse(handedOver.ownershipHealth().continuityAtRisk()); + + for (UUID assetId : List.of(prompt.id(), instruction.id(), pack.id())) { + if (!assetId.equals(pack.id())) { + AssetView covered = assets.assignRole( + AUTHOR, + assetId, + "user", + BACKUP_OWNER_ID.toString(), + AssetRole.BACKUP_OWNER); + assertFalse(covered.ownershipHealth().continuityAtRisk()); + } + assets.assignRole( + AUTHOR, + assetId, + "user", + SUPPORT_AGENT_ID.toString(), + AssetRole.VIEWER); + } + List supportResources = List.of( + ResourceRef.of(ORGANIZATION_ID, "asset", prompt.id()), + ResourceRef.of(ORGANIZATION_ID, "asset", instruction.id()), + ResourceRef.of(ORGANIZATION_ID, "asset", pack.id())); + when(authorizationSets.listAuthorizedResources(any())) + .thenAnswer(invocation -> { + AuthorizedResourceQuery query = invocation.getArgument(0); + return AuthorizedResourceSetResult.resolved( + query.principal().equals(SUPPORT_AGENT.principal()) + ? supportResources + : List.of(), + MODEL_ID); + }); + + var discovery = assistantTools.recommend( + SUPPORT_AGENT, "onboarding", AssetType.CAPABILITY_PACK); + assertEquals(1, discovery.recommendations().size()); + assertEquals( + packRelease.id(), + discovery.recommendations().getFirst().releaseId()); + + PromptEvaluationResult evaluation = prompts.evaluate( + SUPPORT_AGENT, prompt.id(), promptRelease.id()); + assertTrue(evaluation.passed()); + assertEquals(8, evaluation.passedCases()); + PromptRunResult firstCorrectTask = prompts.run( + SUPPORT_AGENT, + prompt.id(), + promptRelease.id(), + Map.of( + "ticket_text", + tickets.getFirst().id() + ": " + tickets.getFirst().text()), + "support SLA escalation", + "golden-poc-first-correct-task"); + assertTrue(firstCorrectTask.output().contains("\"category\":\"billing\"")); + assertEquals(1, firstCorrectTask.citations().size()); + assertEquals( + "SLA and escalation", + firstCorrectTask.citations().getFirst().title()); + + WorkInstructionView acknowledged = instructions.acknowledge( + SUPPORT_AGENT, instruction.id(), instructionRelease.id()); + assertTrue(acknowledged.acknowledged()); + PackJourney journey = packs.start( + SUPPORT_AGENT, pack.id(), packRelease.id()); + for (PackJourney.Item item : journey.items()) { + journey = packs.setItemCompleted( + SUPPORT_AGENT, + pack.id(), + packRelease.id(), + item.key(), + true); + } + assertEquals(PackAssignmentStatus.COMPLETED, journey.status()); + assertEquals(2, journey.completedAccessibleItems()); + + AssetView changedPrompt = assets.updateDraft( + AUTHOR, + prompt.id(), + prompt.draft().lockVersion(), + new AssetDraftInput( + "Asset triage-customer-ticket", + "Replacement Prompt", + "INTERNAL", + "1", + goldenFixture("prompt-template.json").replace( + "Using only approved support policy", + "Using the revised approved support policy"))); + assertNotEquals(prompt.draft().lockVersion(), changedPrompt.draft().lockVersion()); + AssetView replacementSubmission = assets.submit( + AUTHOR, prompt.id(), "Revise support wording"); + approve(prompt.id(), replacementSubmission); + AssetView replacement = assets.publish( + AUTHOR, + prompt.id(), + replacementSubmission.revisions().getFirst().id(), + "2.0.0"); + assertNotEquals( + promptRelease.id(), replacement.releases().getFirst().id()); + assertEquals( + promptRelease.id(), + packs.get(SUPPORT_AGENT, pack.id(), packRelease.id()) + .items() + .stream() + .filter(item -> item.key().equals("prompt")) + .findFirst() + .orElseThrow() + .pinnedVersionId()); + + assets.withdraw( + AUTHOR, + prompt.id(), + promptRelease.id(), + "Replaced by the approved 2.0.0 release"); + assertThrows( + AssetUnavailableException.class, + () -> prompts.run( + SUPPORT_AGENT, + prompt.id(), + promptRelease.id(), + Map.of( + "ticket_text", + tickets.getFirst().id() + + ": " + + tickets.getFirst().text()), + "support SLA escalation", + "golden-poc-withdrawn-release")); + + assertEquals( + 9, + jdbc.queryForObject( + """ + select count(*) + from prompt_runs + where actor_user_id = ? and status = 'SUCCEEDED' + """, + Integer.class, + SUPPORT_AGENT_ID)); + assertEquals( + 1, + jdbc.queryForObject( + """ + select count(*) + from pack_assignments + where actor_user_id = ? and status = 'COMPLETED' + """, + Integer.class, + SUPPORT_AGENT_ID)); + assertEquals( + 1, + jdbc.queryForObject( + """ + select count(*) + from asset_audit_events + where asset_id = ? and event_type = 'RELEASE_WITHDRAWN' + """, + Integer.class, + prompt.id())); + assertTrue(goldenFixture("success-metrics.json") + .contains("\"evaluation_pass\"")); + } + private AssetView create(String slug) { return assets.create( AUTHOR, @@ -1044,9 +1307,62 @@ private static String packPayload( instructionReleaseId); } + private static RetrievedKnowledgeEvidence goldenKnowledgeEvidence() { + return new RetrievedKnowledgeEvidence( + UUID.fromString("90000000-0000-0000-0000-000000000001"), + UUID.fromString("90000000-0000-0000-0000-000000000002"), + UUID.fromString("90000000-0000-0000-0000-000000000003"), + UUID.fromString("90000000-0000-0000-0000-000000000004"), + "SLA and escalation", + "P0 is 15 minutes. P1 is 1 hour. P2 is 4 business hours.", + "fixture://support.sla-and-escalation@1", + null, + null, + "Response tiers", + 1.0, + 1.0, + 1.0, + UUID.fromString("90000000-0000-0000-0000-000000000005"), + UUID.fromString("90000000-0000-0000-0000-000000000005"), + MODEL_ID, + UUID.fromString("90000000-0000-0000-0000-000000000006"), + 1); + } + + private static String goldenFixture(String name) throws IOException { + Path current = Path.of("").toAbsolutePath(); + while (current != null + && !Files.exists(current.resolve("settings.gradle.kts"))) { + current = current.getParent(); + } + if (current == null) { + throw new IllegalStateException("Could not locate the repository root"); + } + return Files.readString(current.resolve( + "demo/fixtures/asset-registry/" + name)); + } + + private record MockTicket( + String id, + String scenario, + String text, + String category, + String slaTier, + boolean escalate, + List allowedCitations, + boolean rubricPass) { + } + private void clearAssetRegistry() { jdbc.execute(""" TRUNCATE TABLE + assistant_asset_feedback, + assistant_asset_traces, + prompt_evaluation_runs, + prompt_runs, + pack_progress, + pack_assignments, + work_instruction_acknowledgements, asset_audit_events, asset_payload_references, asset_relations, diff --git a/apps/api/src/test/resources/db/test-foundation.sql b/apps/api/src/test/resources/db/test-foundation.sql index aa4a8853..68469e0d 100644 --- a/apps/api/src/test/resources/db/test-foundation.sql +++ b/apps/api/src/test/resources/db/test-foundation.sql @@ -56,6 +56,30 @@ INSERT INTO app_users ( now(), now(), 0 + ), + ( + '66666666-6666-6666-6666-666666666666', + '11111111-1111-1111-1111-111111111111', + '33333333-3333-3333-3333-333333333333', + 'An Pham', + 'an@example.test', + 'EMPLOYEE', + true, + now(), + now(), + 0 + ), + ( + '77777777-7777-7777-7777-777777777777', + '11111111-1111-1111-1111-111111111111', + '33333333-3333-3333-3333-333333333333', + 'Bao Le', + 'bao@example.test', + 'TEAM_LEAD', + true, + now(), + now(), + 0 ) ON CONFLICT (id) DO NOTHING; diff --git a/contracts/openapi.json b/contracts/openapi.json index 37a1983a..5c998a3f 100644 --- a/contracts/openapi.json +++ b/contracts/openapi.json @@ -1 +1 @@ -{"openapi":"3.1.0","info":{"title":"OpenAPI definition","version":"v0"},"servers":[{"url":"http://127.0.0.1:8080","description":"Generated server url"}],"paths":{"/api/assistant/tools/assets/{assetId}/releases/{releaseId}/pack/{itemKey}":{"put":{"tags":["assistant-asset-tool-controller"],"summary":"Update actor-derived Pack progress after explicit confirmation","operationId":"updateAssistantPackProgress","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"itemKey","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PackProgressRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PackToolResult"}}}}}}},"/api/assets/{assetId}/releases/{releaseId}/pack-progress/{itemKey}":{"put":{"tags":["asset-consumption-controller"],"summary":"Idempotently update actor-derived progress for one accessible Pack item","operationId":"setCapabilityPackProgress","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"itemKey","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PackProgressRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PackJourney"}}}}}}},"/api/assets/{assetId}/draft":{"put":{"tags":["asset-registry-controller"],"summary":"Update a mutable Asset draft","operationId":"updateAssetDraft","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAssetDraftRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AssetView"}}}}}}},"/api/admin/source-principals/{principalId}/mapping":{"put":{"tags":["admin-source-access-controller"],"summary":"Confirm a principal maps to an internal user","operationId":"confirmAdminSourceMapping","parameters":[{"name":"principalId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmMappingRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AdminSourcePrincipalResponse"}}}}}},"delete":{"tags":["admin-source-access-controller"],"summary":"Revoke a principal's active mapping","operationId":"revokeAdminSourceMapping","parameters":[{"name":"principalId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AdminSourcePrincipalResponse"}}}}}}},"/api/admin/source-connections/identity-trust":{"put":{"tags":["admin-source-access-controller"],"summary":"Record the identity trust for a connection","operationId":"setAdminSourceConnectionTrust","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdentityTrustRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AdminSourceConnectionResponse"}}}}}}},"/api/admin/connectors/{sourceSystem}/{connectionKey}":{"put":{"tags":["admin-connector-controller"],"summary":"Record how a connection is crawled","operationId":"configureAdminConnection","parameters":[{"name":"sourceSystem","in":"path","required":true,"schema":{"type":"string"}},{"name":"connectionKey","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfigureConnectionRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AdminConnectionResponse"}}}}}}},"/api/admin/connectors/{sourceSystem}/{connectionKey}/credential":{"put":{"tags":["admin-connector-controller"],"summary":"Store a credential for a connection","operationId":"setAdminConnectionCredential","parameters":[{"name":"sourceSystem","in":"path","required":true,"schema":{"type":"string"}},{"name":"connectionKey","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectorCredentialRequest"}}},"required":true},"responses":{"204":{"description":"No Content"}}},"delete":{"tags":["admin-connector-controller"],"summary":"Forget a connection's stored credential","operationId":"forgetAdminConnectionCredential","parameters":[{"name":"sourceSystem","in":"path","required":true,"schema":{"type":"string"}},{"name":"connectionKey","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"No Content"}}}},"/api/sources":{"get":{"tags":["source-controller"],"summary":"List sources visible to the current user","operationId":"listSources","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SourceResponse"}}}}}}},"post":{"tags":["source-controller"],"summary":"Upload a source for asynchronous ingestion","operationId":"uploadSource","parameters":[{"name":"classification","in":"query","required":false,"schema":{"type":"string","default":"CONFIDENTIAL","enum":["PUBLIC","INTERNAL","CONFIDENTIAL","RESTRICTED"]}},{"name":"knowledgeSpaceId","in":"query","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"}},"required":["file"]}}}},"responses":{"201":{"description":"Created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SourceResponse"}}}}}}},"/api/knowledge-spaces/{knowledgeSpaceId}/graph/curations/suppressions":{"post":{"tags":["knowledge-graph-management-controller"],"summary":"Delete an effective graph identity without deleting evidence","operationId":"suppressGraphIdentity","parameters":[{"name":"knowledgeSpaceId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuppressIdentityRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"oneOf":[{"$ref":"#/components/schemas/CuratedEntity"},{"$ref":"#/components/schemas/CuratedRelation"},{"$ref":"#/components/schemas/IdentityAlias"},{"$ref":"#/components/schemas/IdentitySuppression"}]}}}}}}},"/api/knowledge-spaces/{knowledgeSpaceId}/graph/curations/relations":{"post":{"tags":["knowledge-graph-management-controller"],"summary":"Create or edit a governed graph relation","operationId":"curateGraphRelation","parameters":[{"name":"knowledgeSpaceId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CurateRelationRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"oneOf":[{"$ref":"#/components/schemas/CuratedEntity"},{"$ref":"#/components/schemas/CuratedRelation"},{"$ref":"#/components/schemas/IdentityAlias"},{"$ref":"#/components/schemas/IdentitySuppression"}]}}}}}}},"/api/knowledge-spaces/{knowledgeSpaceId}/graph/curations/entities":{"post":{"tags":["knowledge-graph-management-controller"],"summary":"Create or edit a governed graph entity","operationId":"curateGraphEntity","parameters":[{"name":"knowledgeSpaceId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CurateEntityRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"oneOf":[{"$ref":"#/components/schemas/CuratedEntity"},{"$ref":"#/components/schemas/CuratedRelation"},{"$ref":"#/components/schemas/IdentityAlias"},{"$ref":"#/components/schemas/IdentitySuppression"}]}}}}}}},"/api/knowledge-spaces/{knowledgeSpaceId}/graph/curations/aliases":{"post":{"tags":["knowledge-graph-management-controller"],"summary":"Merge graph identities through a reversible alias","operationId":"mergeGraphIdentity","parameters":[{"name":"knowledgeSpaceId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AliasIdentityRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"oneOf":[{"$ref":"#/components/schemas/CuratedEntity"},{"$ref":"#/components/schemas/CuratedRelation"},{"$ref":"#/components/schemas/IdentityAlias"},{"$ref":"#/components/schemas/IdentitySuppression"}]}}}}}}},"/api/knowledge-assets/{knowledgeAssetId}/graph-index":{"post":{"tags":["knowledge-asset-lifecycle-controller"],"summary":"Ensure graph indexing uses the current processing profile","operationId":"ensureKnowledgeAssetGraphIndex","parameters":[{"name":"knowledgeAssetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"202":{"description":"Accepted","content":{"*/*":{"schema":{"$ref":"#/components/schemas/GraphIndexJobView"}}}}}}},"/api/knowledge-assets/graph-jobs/{jobId}/resume":{"post":{"tags":["knowledge-asset-lifecycle-controller"],"summary":"Resume unfinished graph indexing","operationId":"resumeGraphIndexJob","parameters":[{"name":"jobId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"202":{"description":"Accepted","content":{"*/*":{"schema":{"$ref":"#/components/schemas/GraphIndexJobView"}}}}}}},"/api/knowledge-assets/graph-jobs/{jobId}/cancel":{"post":{"tags":["knowledge-asset-lifecycle-controller"],"summary":"Cancel queued or in-flight graph indexing","operationId":"cancelGraphIndexJob","parameters":[{"name":"jobId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/GraphIndexJobView"}}}}}}},"/api/assistant/tools/knowledge-search":{"post":{"tags":["assistant-asset-tool-controller"],"summary":"Search canonical permission-aware Knowledge and return citation references","operationId":"searchAssistantKnowledge","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeSearchRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/KnowledgeResult"}}}}}}},"/api/assistant/tools/assets/{assetId}/releases/{releaseId}/prompt-run":{"post":{"tags":["assistant-asset-tool-controller"],"summary":"Run an exact Prompt release after explicit external-provider confirmation","operationId":"runAssistantPrompt","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptRunRequest"}}},"required":true},"responses":{"201":{"description":"Created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PromptRunToolResult"}}}}}}},"/api/assistant/tools/assets/{assetId}/releases/{releaseId}/prompt-render":{"post":{"tags":["assistant-asset-tool-controller"],"summary":"Render an exact Prompt release after variable validation","operationId":"renderAssistantPrompt","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptRenderRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PromptRenderToolResult"}}}}}}},"/api/assistant/tools/assets/{assetId}/releases/{releaseId}/pack":{"get":{"tags":["assistant-asset-tool-controller"],"summary":"Read an actor-scoped exact Pack journey","operationId":"readAssistantPack","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PackToolResult"}}}}}},"post":{"tags":["assistant-asset-tool-controller"],"summary":"Start an exact Pack after explicit state-change confirmation","operationId":"startAssistantPack","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmedActionRequest"}}},"required":true},"responses":{"201":{"description":"Created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PackToolResult"}}}}}}},"/api/assistant/tools/assets/{assetId}/releases/{releaseId}/fork":{"post":{"tags":["assistant-asset-tool-controller"],"summary":"Fork an exact release after explicit draft-creation confirmation","operationId":"forkAssistantAssetRelease","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForkRequest"}}},"required":true},"responses":{"201":{"description":"Created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ForkResult"}}}}}}},"/api/assistant/tools/assets/{assetId}/releases/{releaseId}/feedback":{"post":{"tags":["assistant-asset-tool-controller"],"summary":"Submit feedback against an exact release after explicit confirmation","operationId":"submitAssistantAssetFeedback","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackRequest"}}},"required":true},"responses":{"201":{"description":"Created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/FeedbackResult"}}}}}}},"/api/assistant/chat":{"post":{"tags":["assistant-controller"],"summary":"Stream an answer from permission-verified knowledge","operationId":"streamAssistantChat","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssistantChatRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"text/event-stream":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ServerSentEventString"}}}}}}}},"/api/assets":{"get":{"tags":["asset-registry-controller"],"summary":"List released or authoring Assets visible to the actor","operationId":"listAssets","parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string"}},{"name":"type","in":"query","required":false,"schema":{"type":"string","enum":["PROMPT_TEMPLATE","WORK_INSTRUCTION","CAPABILITY_PACK"]}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AssetSummary"}}}}}}},"post":{"tags":["asset-registry-controller"],"summary":"Create an Asset and its mutable draft","operationId":"createAsset","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAssetRequest"}}},"required":true},"responses":{"201":{"description":"Created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AssetView"}}}}}}},"/api/assets/{assetId}/submissions":{"post":{"tags":["asset-registry-controller"],"summary":"Submit an immutable Asset revision for review","operationId":"submitAssetRevision","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitAssetRevisionRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AssetView"}}}}}}},"/api/assets/{assetId}/role-assignments":{"post":{"tags":["asset-registry-controller"],"summary":"Assign an accountable role on an Asset","operationId":"assignAssetRole","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssignAssetRoleRequest"}}},"required":true},"responses":{"201":{"description":"Created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AssetView"}}}}}}},"/api/assets/{assetId}/reviews/{reviewCaseId}/decisions":{"post":{"tags":["asset-registry-controller"],"summary":"Record a decision against an exact revision digest","operationId":"decideAssetReview","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"reviewCaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetReviewDecisionRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AssetView"}}}}}}},"/api/assets/{assetId}/releases":{"post":{"tags":["asset-registry-controller"],"summary":"Publish an approved immutable Asset revision","operationId":"publishAssetRelease","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublishAssetReleaseRequest"}}},"required":true},"responses":{"201":{"description":"Created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AssetView"}}}}}}},"/api/assets/{assetId}/releases/{releaseId}/work-instruction/acknowledgement":{"post":{"tags":["asset-consumption-controller"],"summary":"Idempotently acknowledge an exact Work Instruction release","operationId":"acknowledgeWorkInstruction","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/WorkInstructionView"}}}}}}},"/api/assets/{assetId}/releases/{releaseId}/withdrawal":{"post":{"tags":["asset-registry-controller"],"summary":"Withdraw an Asset release from new use","operationId":"withdrawAssetRelease","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetAvailabilityRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AssetView"}}}}}}},"/api/assets/{assetId}/releases/{releaseId}/prompt/runs":{"post":{"tags":["asset-consumption-controller"],"summary":"Run an exact Prompt release through the provider-neutral AI gateway","operationId":"runPromptRelease","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptRunRequest"}}},"required":true},"responses":{"201":{"description":"Created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PromptRunResult"}}}}}}},"/api/assets/{assetId}/releases/{releaseId}/prompt/render":{"post":{"tags":["asset-consumption-controller"],"summary":"Deterministically render an exact authorized Prompt release","operationId":"renderPromptRelease","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptVariablesRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PromptRenderResult"}}}}}}},"/api/assets/{assetId}/releases/{releaseId}/prompt/evaluations":{"post":{"tags":["asset-consumption-controller"],"summary":"Run the bounded evaluation cases pinned in a Prompt release","operationId":"evaluatePromptRelease","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"201":{"description":"Created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PromptEvaluationResult"}}}}}}},"/api/assets/{assetId}/releases/{releaseId}/pack-assignment":{"post":{"tags":["asset-consumption-controller"],"summary":"Start or resume an exact authorized Capability Pack release","operationId":"startCapabilityPack","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"201":{"description":"Created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PackJourney"}}}}}}},"/api/assets/{assetId}/releases/{releaseId}/forks":{"post":{"tags":["asset-consumption-controller"],"summary":"Fork an exact authorized release into a new mutable draft","operationId":"forkAssetRelease","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForkReleaseRequest"}}},"required":true},"responses":{"201":{"description":"Created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AssetView"}}}}}}},"/api/assets/{assetId}/releases/{releaseId}/deprecation":{"post":{"tags":["asset-registry-controller"],"summary":"Deprecate an Asset release","operationId":"deprecateAssetRelease","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetAvailabilityRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AssetView"}}}}}}},"/api/assets/{assetId}/prompt/evaluation-comparisons":{"post":{"tags":["asset-consumption-controller"],"summary":"Compare bounded evaluation results for two exact Prompt releases","operationId":"comparePromptReleases","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptComparisonRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PromptEvaluationComparison"}}}}}}},"/api/asset-delivery/{assetId}/releases/{releaseId}/prompt-render":{"post":{"tags":["asset-delivery-controller"],"summary":"Deterministically render an exact authorized Prompt release","operationId":"renderReleasedPrompt","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptVariablesRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PromptRenderResult"}}}}}}},"/api/admin/roles/{role}/members":{"post":{"tags":["admin-role-controller"],"summary":"Assign a user to a role","operationId":"assignAdminRole","parameters":[{"name":"role","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssignRoleRequest"}}},"required":true},"responses":{"204":{"description":"No Content"}}}},"/api/admin/knowledge-spaces":{"get":{"tags":["admin-knowledge-space-controller"],"summary":"List Knowledge Spaces and the grants stored against them","operationId":"listAdminKnowledgeSpaces","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AdminKnowledgeSpaceResponse"}}}}}}},"post":{"tags":["admin-knowledge-space-controller"],"summary":"Create a Knowledge Space","operationId":"createAdminKnowledgeSpace","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateKnowledgeSpaceRequest"}}},"required":true},"responses":{"201":{"description":"Created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AdminKnowledgeSpaceResponse"}}}}}}},"/api/admin/knowledge-spaces/{knowledgeSpaceId}/grants":{"post":{"tags":["admin-knowledge-space-controller"],"summary":"Grant a subject access to a Knowledge Space","operationId":"grantAdminKnowledgeSpaceAccess","parameters":[{"name":"knowledgeSpaceId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantKnowledgeSpaceAccessRequest"}}},"required":true},"responses":{"204":{"description":"No Content"}}},"delete":{"tags":["admin-knowledge-space-controller"],"summary":"Revoke a subject's access to a Knowledge Space","operationId":"revokeAdminKnowledgeSpaceAccess","parameters":[{"name":"knowledgeSpaceId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"relation","in":"query","required":true,"schema":{"type":"string"}},{"name":"kind","in":"query","required":true,"schema":{"type":"string","enum":["ORGANIZATION","DEPARTMENT","DEPARTMENT_MANAGERS","ROLE","USER"]}},{"name":"subjectId","in":"query","required":false,"schema":{"type":"string","format":"uuid"}},{"name":"role","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"204":{"description":"No Content"}}}},"/api/admin/invitations":{"get":{"tags":["admin-invitation-controller"],"summary":"List invited addresses and their status","operationId":"listAdminInvitations","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AdminInvitationResponse"}}}}}}},"post":{"tags":["admin-invitation-controller"],"summary":"Expect an address to sign in","operationId":"createAdminInvitation","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateInvitationRequest"}}},"required":true},"responses":{"201":{"description":"Created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AdminInvitationResponse"}}}}}}},"/api/admin/connectors/{sourceSystem}/{connectionKey}/test":{"post":{"tags":["admin-connector-controller"],"summary":"Check a connection's stored credential","operationId":"testAdminConnection","parameters":[{"name":"sourceSystem","in":"path","required":true,"schema":{"type":"string"}},{"name":"connectionKey","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AdminConnectorProbeResponse"}}}}}}},"/api/admin/connectors/{sourceSystem}/{connectionKey}/crawl":{"post":{"tags":["admin-connector-controller"],"summary":"Ask for a content crawl on the next poll","operationId":"requestAdminConnectionCrawl","parameters":[{"name":"sourceSystem","in":"path","required":true,"schema":{"type":"string"}},{"name":"connectionKey","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"202":{"description":"Accepted"}}}},"/api/admin/connectors/{sourceSystem}/test":{"post":{"tags":["admin-connector-controller"],"summary":"Check a credential without storing it","operationId":"testAdminConnectorCredential","parameters":[{"name":"sourceSystem","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectorCredentialRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AdminConnectorProbeResponse"}}}}}}},"/api/admin/access/explain":{"post":{"tags":["admin-permission-controller"],"summary":"Answer whether a user holds a permission on one resource, and by which derivation","operationId":"explainAdminAccess","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExplainAccessRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ExplainAccessResponse"}}}}}}},"/api/assistant/conversations/{conversationId}":{"delete":{"tags":["assistant-controller"],"summary":"Delete the current actor's transcript and bounded model memory","operationId":"deleteAssistantConversation","parameters":[{"name":"conversationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"No Content"}}},"patch":{"tags":["assistant-controller"],"summary":"Rename the current actor's conversation","operationId":"renameAssistantConversation","parameters":[{"name":"conversationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenameConversationRequest"}}},"required":true},"responses":{"204":{"description":"No Content"}}}},"/api/admin/users/{userId}":{"patch":{"tags":["admin-user-controller"],"summary":"Change a user's role or activation","operationId":"updateAdminUser","parameters":[{"name":"userId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAdminUserRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AdminUserResponse"}}}}}}},"/api/session":{"get":{"tags":["browser-session-controller"],"summary":"Read the current browser session","operationId":"getBrowserSession","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SessionResponse"}}}}}}},"/api/session/csrf":{"get":{"tags":["browser-session-controller"],"summary":"Issue a CSRF token for browser mutations","operationId":"getBrowserCsrfToken","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CsrfResponse"}}}}}}},"/api/organization/context":{"get":{"tags":["organization-context-controller"],"operationId":"context","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OrganizationContextResponse"}}}}}}},"/api/me":{"get":{"tags":["me-controller"],"operationId":"me","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MeResponse"}}}}}}},"/api/knowledge/search":{"get":{"tags":["knowledge-search-controller"],"summary":"Search permission-verified knowledge evidence","operationId":"searchKnowledge","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/KnowledgeSearchResponse"}}}}}}},"/api/knowledge/catalog":{"get":{"tags":["knowledge-catalog-controller"],"summary":"List current permission-verified Knowledge versions for composition","operationId":"listKnowledgeCatalog","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/KnowledgeCatalogItem"}}}}}}}},"/api/knowledge-spaces/{knowledgeSpaceId}/graph/export":{"get":{"tags":["knowledge-graph-management-controller"],"summary":"Export only graph evidence visible to the current user","operationId":"exportKnowledgeGraph","parameters":[{"name":"knowledgeSpaceId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"format","in":"query","required":false,"schema":{"type":"string","default":"JSON","enum":["JSON","CSV","MARKDOWN","TEXT"]}},{"name":"X-Request-Id","in":"header","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"string"}}}}}}},"/api/knowledge-spaces/{knowledgeSpaceId}/graph/explorer":{"get":{"tags":["knowledge-graph-explorer-controller"],"summary":"Read a bounded permission-filtered graph view","operationId":"exploreKnowledgeGraph","parameters":[{"name":"knowledgeSpaceId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"q","in":"query","required":false,"schema":{"type":"string"}},{"name":"entityLimit","in":"query","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"maxDepth","in":"query","required":false,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/KnowledgeGraphView"}}}}}}},"/api/knowledge-spaces/visible":{"get":{"tags":["knowledge-space-controller"],"summary":"List Knowledge Spaces visible to the current user","operationId":"listVisibleKnowledgeSpaces","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/KnowledgeSpaceResponse"}}}}}}}},"/api/knowledge-spaces/upload-targets":{"get":{"tags":["knowledge-space-controller"],"summary":"List Knowledge Spaces where the current user may add knowledge","operationId":"listKnowledgeSpaceUploadTargets","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/KnowledgeSpaceResponse"}}}}}}}},"/api/knowledge-assets/graph-jobs/{jobId}":{"get":{"tags":["knowledge-asset-lifecycle-controller"],"summary":"Read graph indexing lifecycle status","operationId":"getGraphIndexJob","parameters":[{"name":"jobId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/GraphIndexJobView"}}}}}}},"/api/health":{"get":{"tags":["health-controller"],"operationId":"health","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"object","additionalProperties":{"type":"string"}}}}}}}},"/api/citations/{chunkId}/content":{"get":{"tags":["citation-content-controller"],"summary":"Stream permission-verified source evidence","operationId":"readCitationContent","parameters":[{"name":"chunkId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/StreamingResponseBody"}}}}}}},"/api/assistant/tools/assets/{assetId}/releases/{releaseId}/work-instruction":{"get":{"tags":["assistant-asset-tool-controller"],"summary":"Guide an exact Work Instruction release","operationId":"followAssistantWorkInstruction","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/WorkInstructionToolResult"}}}}}}},"/api/assistant/tools/assets/{assetId}/releases/{releaseId}/prompt-form":{"get":{"tags":["assistant-asset-tool-controller"],"summary":"Resolve the variables required by an exact Prompt release","operationId":"prepareAssistantPrompt","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PromptFormResult"}}}}}}},"/api/assistant/tools/asset-recommendations":{"get":{"tags":["assistant-asset-tool-controller"],"summary":"Recommend exact usable Asset releases without leaking denied candidates","operationId":"recommendAssistantAssets","parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string"}},{"name":"type","in":"query","required":false,"schema":{"type":"string","enum":["PROMPT_TEMPLATE","WORK_INSTRUCTION","CAPABILITY_PACK"]}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/RecommendationResult"}}}}}}},"/api/assistant/conversations":{"get":{"tags":["assistant-controller"],"summary":"List the current actor's conversations by recent activity","operationId":"listAssistantConversations","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AssistantConversationSummary"}}}}}}}},"/api/assistant/conversations/{conversationId}/messages":{"get":{"tags":["assistant-controller"],"summary":"Replay a tenant- and actor-scoped full conversation transcript","operationId":"getAssistantConversationHistory","parameters":[{"name":"conversationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AssistantConversationMessageView"}}}}}}}},"/api/assets/{assetId}":{"get":{"tags":["asset-registry-controller"],"summary":"Read an authorized Asset and its governance history","operationId":"getAsset","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AssetView"}}}}}}},"/api/assets/{assetId}/releases/{releaseId}/work-instruction":{"get":{"tags":["asset-consumption-controller"],"summary":"Follow an exact authorized Work Instruction release","operationId":"followWorkInstruction","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/WorkInstructionView"}}}}}}},"/api/assets/{assetId}/releases/{releaseId}/pack-journey":{"get":{"tags":["asset-consumption-controller"],"summary":"Read an actor-scoped Capability Pack journey","operationId":"getCapabilityPackJourney","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PackJourney"}}}}}}},"/api/asset-delivery":{"get":{"tags":["asset-delivery-controller"],"summary":"Search exact released Assets authorized for the current actor","operationId":"searchReleasedAssets","parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string"}},{"name":"type","in":"query","required":false,"schema":{"type":"string","enum":["PROMPT_TEMPLATE","WORK_INSTRUCTION","CAPABILITY_PACK"]}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AssetRecommendation"}}}}}}}},"/api/asset-delivery/{assetId}":{"get":{"tags":["asset-delivery-controller"],"summary":"Read the latest usable immutable release for an Asset","operationId":"getLatestReleasedAsset","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AssetDeliveryRelease"}}}}}}},"/api/asset-delivery/{assetId}/releases/{releaseId}":{"get":{"tags":["asset-delivery-controller"],"summary":"Read one exact usable immutable Asset release","operationId":"getReleasedAsset","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AssetDeliveryRelease"}}}}}}},"/api/asset-delivery/{assetId}/releases/{releaseId}/relations":{"get":{"tags":["asset-delivery-controller"],"summary":"Resolve only independently authorized relations of an exact release","operationId":"resolveReleasedAssetRelations","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AssetRelationResolution"}}}}}}},"/api/asset-delivery/{assetId}/releases/{releaseId}/pack":{"get":{"tags":["asset-delivery-controller"],"summary":"Read a Pack definition with independently authorized pinned items","operationId":"getReleasedCapabilityPack","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CapabilityPackDefinition"}}}}}}},"/api/admin/users":{"get":{"tags":["admin-user-controller"],"summary":"List internal users with their sign-in and mapping status","operationId":"listAdminUsers","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AdminUserResponse"}}}}}}}},"/api/admin/users/{userId}/permissions":{"get":{"tags":["admin-permission-controller"],"summary":"Resolve a user's organization permissions as the engine currently answers them","operationId":"listAdminUserPermissions","parameters":[{"name":"userId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/EffectivePermissionResponse"}}}}}}},"/api/admin/source-principals":{"get":{"tags":["admin-source-access-controller"],"summary":"List observed principals and their mapping","operationId":"listAdminSourcePrincipals","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AdminSourcePrincipalResponse"}}}}}}}},"/api/admin/source-groups":{"get":{"tags":["admin-source-access-controller"],"summary":"List source groups with their sealed membership","operationId":"listAdminSourceGroups","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AdminSourceGroupResponse"}}}}}}}},"/api/admin/source-connections":{"get":{"tags":["admin-source-access-controller"],"summary":"List observed connections and their trust level","operationId":"listAdminSourceConnections","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AdminSourceConnectionResponse"}}}}}}}},"/api/admin/roles":{"get":{"tags":["admin-role-controller"],"summary":"List roles and who is assigned to them","operationId":"listAdminRoles","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AdminRoleListResponse"}}}}}}},"/api/admin/knowledge-spaces/grant-options":{"get":{"tags":["admin-knowledge-space-controller"],"summary":"List the subject shapes each Knowledge Space relation accepts","operationId":"listAdminKnowledgeSpaceGrantOptions","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/KnowledgeSpaceGrantOptionResponse"}}}}}}}},"/api/admin/connectors/{sourceSystem}":{"get":{"tags":["admin-connector-controller"],"summary":"List a source's connections and their crawl settings","operationId":"listAdminConnections","parameters":[{"name":"sourceSystem","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AdminConnectionResponse"}}}}}}}},"/api/admin/connectors/{sourceSystem}/{connectionKey}/scopes":{"get":{"tags":["admin-connector-controller"],"summary":"List what a connection can be pointed at","operationId":"listAdminConnectionScopes","parameters":[{"name":"sourceSystem","in":"path","required":true,"schema":{"type":"string"}},{"name":"connectionKey","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AdminConnectorScopeResponse"}}}}}}}},"/api/admin/connectors/{sourceSystem}/{connectionKey}/activity":{"get":{"tags":["admin-connector-controller"],"summary":"Read what a connection has crawled and what went wrong","operationId":"getAdminConnectionActivity","parameters":[{"name":"sourceSystem","in":"path","required":true,"schema":{"type":"string"}},{"name":"connectionKey","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AdminConnectionActivityResponse"}}}}}}},"/api/admin/connectors/sources":{"get":{"tags":["admin-connector-controller"],"summary":"List the sources this deployment can ingest","operationId":"listAdminConnectorSources","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AdminConnectorSourceResponse"}}}}}}}},"/api/knowledge-spaces/{knowledgeSpaceId}/graph/curations/{curationId}":{"delete":{"tags":["knowledge-graph-management-controller"],"summary":"Reverse a graph curation record","operationId":"deactivateGraphCuration","parameters":[{"name":"knowledgeSpaceId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"curationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"authorizationGeneration","in":"query","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"reason","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}}},"/api/knowledge-assets/{knowledgeAssetId}":{"delete":{"tags":["knowledge-asset-lifecycle-controller"],"summary":"Retire a Knowledge Asset and remove its derived graph","operationId":"deleteKnowledgeAsset","parameters":[{"name":"knowledgeAssetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/KnowledgeAssetRef"}}}}}}},"/api/admin/roles/{role}/members/{userId}":{"delete":{"tags":["admin-role-controller"],"summary":"Remove a user from a role","operationId":"revokeAdminRole","parameters":[{"name":"role","in":"path","required":true,"schema":{"type":"string"}},{"name":"userId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"No Content"}}}},"/api/admin/invitations/{invitationId}":{"delete":{"tags":["admin-invitation-controller"],"summary":"Withdraw an invitation that has not been used","operationId":"revokeAdminInvitation","parameters":[{"name":"invitationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"No Content"}}}}},"components":{"schemas":{"PackProgressRequest":{"type":"object","properties":{"completed":{"type":"boolean"},"confirmed":{"type":"boolean"}}},"Item":{"type":"object","properties":{"key":{"type":"string"},"required":{"type":"boolean"},"order":{"type":"integer","format":"int32"},"kind":{"type":"string"},"resourceId":{"type":"string","format":"uuid"},"pinnedVersionId":{"type":"string","format":"uuid"},"title":{"type":"string"},"versionLabel":{"type":"string"},"availability":{"type":"string","enum":["AVAILABLE","DEPRECATED","WITHDRAWN"]},"completed":{"type":"boolean"},"completedAt":{"type":"string","format":"date-time"}}},"PackJourney":{"type":"object","properties":{"assignmentId":{"type":"string","format":"uuid"},"packAssetId":{"type":"string","format":"uuid"},"packReleaseId":{"type":"string","format":"uuid"},"releaseDigest":{"type":"string"},"title":{"type":"string"},"versionLabel":{"type":"string"},"purpose":{"type":"string","enum":["ROLE_ONBOARDING","HANDOVER","ROLE_ENABLEMENT"]},"audience":{"type":"string"},"expectedOutcome":{"type":"string"},"status":{"type":"string","enum":["IN_PROGRESS","COMPLETED"]},"accessGap":{"type":"boolean"},"completedAccessibleItems":{"type":"integer","format":"int32"},"items":{"type":"array","items":{"$ref":"#/components/schemas/Item"}},"startedAt":{"type":"string","format":"date-time"},"completedAt":{"type":"string","format":"date-time"}}},"PackToolResult":{"type":"object","properties":{"traceId":{"type":"string","format":"uuid"},"journey":{"$ref":"#/components/schemas/PackJourney"}}},"UpdateAssetDraftRequest":{"type":"object","properties":{"expectedLockVersion":{"type":"integer","format":"int64"},"title":{"type":"string"},"summary":{"type":"string"},"classification":{"type":"string"},"schemaVersion":{"type":"string"},"payload":{"type":"string"}}},"AssetView":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["PROMPT_TEMPLATE","WORK_INSTRUCTION","CAPABILITY_PACK"]},"namespace":{"type":"string"},"slug":{"type":"string"},"knowledgeSpaceId":{"type":"string","format":"uuid"},"portfolioState":{"type":"string","enum":["DRAFT_ONLY","ACTIVE","SUNSETTING","RETIRED"]},"authorizationReady":{"type":"boolean"},"draft":{"$ref":"#/components/schemas/Draft"},"revisions":{"type":"array","items":{"$ref":"#/components/schemas/Revision"}},"reviews":{"type":"array","items":{"$ref":"#/components/schemas/Review"}},"releases":{"type":"array","items":{"$ref":"#/components/schemas/Release"}},"roleAssignments":{"type":"array","items":{"$ref":"#/components/schemas/RoleAssignment"}}}},"AvailabilityEvent":{"type":"object","properties":{"availability":{"type":"string","enum":["AVAILABLE","DEPRECATED","WITHDRAWN"]},"reason":{"type":"string"},"changedByUserId":{"type":"string","format":"uuid"},"effectiveAt":{"type":"string","format":"date-time"}}},"Decision":{"type":"object","properties":{"reviewerUserId":{"type":"string","format":"uuid"},"decision":{"type":"string","enum":["REQUEST_CHANGES","REJECT","APPROVE","CANCEL"]},"comment":{"type":"string"},"decidedAt":{"type":"string","format":"date-time"}}},"Draft":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"lockVersion":{"type":"integer","format":"int64"},"title":{"type":"string"},"summary":{"type":"string"},"classification":{"type":"string"},"schemaVersion":{"type":"string"},"payload":{"type":"string"},"editedByUserId":{"type":"string","format":"uuid"},"updatedAt":{"type":"string","format":"date-time"}}},"Release":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"revisionId":{"type":"string","format":"uuid"},"sequence":{"type":"integer","format":"int64"},"versionLabel":{"type":"string"},"title":{"type":"string"},"summary":{"type":"string"},"classification":{"type":"string"},"schemaVersion":{"type":"string"},"payload":{"type":"string"},"digest":{"type":"string"},"releasedByUserId":{"type":"string","format":"uuid"},"releasedAt":{"type":"string","format":"date-time"},"availability":{"type":"string","enum":["AVAILABLE","DEPRECATED","WITHDRAWN"]},"availabilityHistory":{"type":"array","items":{"$ref":"#/components/schemas/AvailabilityEvent"}}}},"Review":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"revisionId":{"type":"string","format":"uuid"},"revisionDigest":{"type":"string"},"state":{"type":"string","enum":["IN_REVIEW","CHANGES_REQUESTED","REJECTED","CANCELLED","APPROVED"]},"policyVersion":{"type":"string"},"requestedByUserId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"resolvedAt":{"type":"string","format":"date-time"},"decisions":{"type":"array","items":{"$ref":"#/components/schemas/Decision"}}}},"Revision":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"sequence":{"type":"integer","format":"int64"},"title":{"type":"string"},"summary":{"type":"string"},"classification":{"type":"string"},"schemaVersion":{"type":"string"},"payload":{"type":"string"},"digest":{"type":"string"},"changeNote":{"type":"string"},"createdByUserId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"}}},"RoleAssignment":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"principalType":{"type":"string"},"principalId":{"type":"string"},"role":{"type":"string","enum":["OWNER","BACKUP_OWNER","STEWARD","VIEWER","EDITOR","REVIEWER","PUBLISHER"]},"validFrom":{"type":"string","format":"date-time"},"validUntil":{"type":"string","format":"date-time"},"assignedByUserId":{"type":"string","format":"uuid"},"projectedAt":{"type":"string","format":"date-time"}}},"ConfirmMappingRequest":{"type":"object","properties":{"appUserId":{"type":"string","format":"uuid"}}},"AdminSourceMappingResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"appUserId":{"type":"string","format":"uuid"},"appUserName":{"type":"string"},"appUserEmail":{"type":"string"},"method":{"type":"string","enum":["IDP_JOIN","SSO_EMAIL_JOIN","SELF_CLAIM","ADMIN_CONFIRMED"]},"status":{"type":"string","enum":["ACTIVE","REVOKED"]},"evidence":{"type":"string"},"verifiedAt":{"type":"string","format":"date-time"}}},"AdminSourcePrincipalResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"sourceSystem":{"type":"string"},"sourceConnectionKey":{"type":"string"},"externalKey":{"type":"string"},"kind":{"type":"string","enum":["SOURCE_USER","SOURCE_GROUP"]},"observedEmail":{"type":"string"},"observedDisplayName":{"type":"string"},"ssoVerified":{"type":"boolean"},"lastSeenAt":{"type":"string","format":"date-time"},"mapping":{"$ref":"#/components/schemas/AdminSourceMappingResponse"}}},"IdentityTrustRequest":{"type":"object","properties":{"sourceSystem":{"type":"string"},"sourceConnectionKey":{"type":"string"},"identityTrust":{"type":"string","enum":["UNTRUSTED","SSO_VERIFIED"]}}},"AdminSourceConnectionResponse":{"type":"object","properties":{"sourceSystem":{"type":"string"},"sourceConnectionKey":{"type":"string"},"identityTrust":{"type":"string","enum":["UNTRUSTED","SSO_VERIFIED"]},"trustDecidedByUserId":{"type":"string","format":"uuid"},"trustDecidedAt":{"type":"string","format":"date-time"},"userCount":{"type":"integer","format":"int32"},"mappedUserCount":{"type":"integer","format":"int32"},"unmappedUserCount":{"type":"integer","format":"int32"},"groupCount":{"type":"integer","format":"int32"},"lastSeenAt":{"type":"string","format":"date-time"}}},"ConfigureConnectionRequest":{"type":"object","properties":{"crawlEnabled":{"type":"boolean"},"knowledgeSpaceId":{"type":"string","format":"uuid"},"actorUserId":{"type":"string","format":"uuid"},"sourceConfig":{"type":"object","additionalProperties":{}},"contentCrawlIntervalSeconds":{"type":"integer","format":"int64"}}},"AdminConnectionResponse":{"type":"object","properties":{"sourceSystem":{"type":"string"},"sourceConnectionKey":{"type":"string"},"identityTrust":{"type":"string","enum":["UNTRUSTED","SSO_VERIFIED"]},"crawlEnabled":{"type":"boolean"},"knowledgeSpaceId":{"type":"string","format":"uuid"},"actorUserId":{"type":"string","format":"uuid"},"sourceConfig":{"type":"object","additionalProperties":{}},"contentCrawlIntervalSeconds":{"type":"integer","format":"int64"},"credentialSet":{"type":"boolean"},"credentialSetByUserId":{"type":"string","format":"uuid"},"credentialSetAt":{"type":"string","format":"date-time"},"configuredByUserId":{"type":"string","format":"uuid"},"configuredAt":{"type":"string","format":"date-time"}}},"ConnectorCredentialRequest":{"type":"object","properties":{"credential":{"type":"string"}}},"SourceResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"sourceSystem":{"type":"string"},"aclAuthority":{"type":"string"},"status":{"type":"string"},"classification":{"type":"string"},"fileName":{"type":"string"},"mediaType":{"type":"string"},"contentLength":{"type":"integer","format":"int64"},"failureCode":{"type":"string"},"failureMessage":{"type":"string"},"embeddingProfileKey":{"type":"string"},"embeddingProvider":{"type":"string"},"embeddingModel":{"type":"string"},"embeddingDimensions":{"type":"integer","format":"int32"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"SuppressIdentityRequest":{"type":"object","properties":{"idempotencyKey":{"type":"string"},"reason":{"type":"string"},"authorizationGeneration":{"type":"integer","format":"int64"},"kind":{"type":"string","enum":["ENTITY","RELATION"]},"identityId":{"type":"string","format":"uuid"}}},"CuratedEntity":{"allOf":[{"$ref":"#/components/schemas/GraphCurationRecord"},{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"namespace":{"$ref":"#/components/schemas/ProjectionNamespace"},"entity":{"$ref":"#/components/schemas/GraphIdentityRef"},"name":{"type":"string"},"type":{"type":"string"},"description":{"type":"string"},"governingEvidence":{"$ref":"#/components/schemas/EvidenceReference"},"provenance":{"$ref":"#/components/schemas/CurationProvenance"}}}]},"CuratedRelation":{"allOf":[{"$ref":"#/components/schemas/GraphCurationRecord"},{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"namespace":{"$ref":"#/components/schemas/ProjectionNamespace"},"relation":{"$ref":"#/components/schemas/GraphIdentityRef"},"sourceEntity":{"$ref":"#/components/schemas/GraphIdentityRef"},"targetEntity":{"$ref":"#/components/schemas/GraphIdentityRef"},"type":{"type":"string"},"keywords":{"type":"array","items":{"type":"string"}},"description":{"type":"string"},"weight":{"type":"number","format":"double"},"governingEvidence":{"$ref":"#/components/schemas/EvidenceReference"},"provenance":{"$ref":"#/components/schemas/CurationProvenance"}}}]},"CurationProvenance":{"type":"object","properties":{"actorUserId":{"type":"string","format":"uuid"},"authorizationModelId":{"type":"string"},"aclGeneration":{"type":"integer","format":"int64"},"curatedAt":{"type":"string","format":"date-time"},"reason":{"type":"string"}}},"EvidenceReference":{"type":"object","properties":{"organizationId":{"type":"string","format":"uuid"},"knowledgeAssetId":{"type":"string","format":"uuid"},"sourceRevisionId":{"type":"string","format":"uuid"},"chunkId":{"type":"string","format":"uuid"},"aclSnapshotId":{"type":"string","format":"uuid"},"aclGeneration":{"type":"integer","format":"int64"},"chunk":{"type":"boolean"}}},"GraphCurationRecord":{},"GraphIdentityRef":{"type":"object","properties":{"kind":{"type":"string","enum":["ENTITY","RELATION"]},"id":{"type":"string","format":"uuid"}}},"IdentityAlias":{"allOf":[{"$ref":"#/components/schemas/GraphCurationRecord"},{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"namespace":{"$ref":"#/components/schemas/ProjectionNamespace"},"source":{"$ref":"#/components/schemas/GraphIdentityRef"},"target":{"$ref":"#/components/schemas/GraphIdentityRef"},"provenance":{"$ref":"#/components/schemas/CurationProvenance"}}}]},"IdentitySuppression":{"allOf":[{"$ref":"#/components/schemas/GraphCurationRecord"},{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"namespace":{"$ref":"#/components/schemas/ProjectionNamespace"},"identity":{"$ref":"#/components/schemas/GraphIdentityRef"},"provenance":{"$ref":"#/components/schemas/CurationProvenance"}}}]},"ProjectionNamespace":{"type":"object","properties":{"organizationId":{"type":"string","format":"uuid"},"workspace":{"type":"string"},"collection":{"type":"string"}}},"CurateRelationRequest":{"type":"object","properties":{"idempotencyKey":{"type":"string"},"reason":{"type":"string"},"authorizationGeneration":{"type":"integer","format":"int64"},"relationId":{"type":"string","format":"uuid"},"sourceEntityId":{"type":"string","format":"uuid"},"targetEntityId":{"type":"string","format":"uuid"},"type":{"type":"string"},"keywords":{"type":"array","items":{"type":"string"}},"description":{"type":"string"},"weight":{"type":"number","format":"double"},"evidence":{"$ref":"#/components/schemas/EvidenceRequest"}}},"EvidenceRequest":{"type":"object","properties":{"organizationId":{"type":"string","format":"uuid"},"knowledgeAssetId":{"type":"string","format":"uuid"},"sourceRevisionId":{"type":"string","format":"uuid"},"chunkId":{"type":"string","format":"uuid"},"aclSnapshotId":{"type":"string","format":"uuid"},"aclGeneration":{"type":"integer","format":"int64"}}},"CurateEntityRequest":{"type":"object","properties":{"idempotencyKey":{"type":"string"},"reason":{"type":"string"},"authorizationGeneration":{"type":"integer","format":"int64"},"entityId":{"type":"string","format":"uuid"},"name":{"type":"string"},"type":{"type":"string"},"description":{"type":"string"},"evidence":{"$ref":"#/components/schemas/EvidenceRequest"}}},"AliasIdentityRequest":{"type":"object","properties":{"idempotencyKey":{"type":"string"},"reason":{"type":"string"},"authorizationGeneration":{"type":"integer","format":"int64"},"kind":{"type":"string","enum":["ENTITY","RELATION"]},"sourceIdentityId":{"type":"string","format":"uuid"},"targetIdentityId":{"type":"string","format":"uuid"}}},"GraphIndexJobView":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"knowledgeAssetId":{"type":"string","format":"uuid"},"knowledgeAssetVersionId":{"type":"string","format":"uuid"},"sourceRevisionId":{"type":"string","format":"uuid"},"projectionGeneration":{"type":"integer","format":"int64"},"graphProcessingProfileId":{"type":"string","format":"uuid"},"graphProcessingProfileSha256":{"type":"string"},"status":{"type":"string"},"attempt":{"type":"integer","format":"int32"},"cancellationRequested":{"type":"boolean"},"cancellationRequestedAt":{"type":"string","format":"date-time"},"lastErrorCode":{"type":"string"},"lastErrorMessage":{"type":"string"},"completedAt":{"type":"string","format":"date-time"}}},"KnowledgeSearchRequest":{"type":"object","properties":{"query":{"type":"string"},"requestId":{"type":"string"}}},"KnowledgeCitation":{"type":"object","properties":{"chunkId":{"type":"string","format":"uuid"},"knowledgeAssetId":{"type":"string","format":"uuid"},"sourceRevisionId":{"type":"string","format":"uuid"},"title":{"type":"string"},"startPage":{"type":"integer","format":"int32"},"endPage":{"type":"integer","format":"int32"},"heading":{"type":"string"}}},"KnowledgeResult":{"type":"object","properties":{"traceId":{"type":"string","format":"uuid"},"requestId":{"type":"string"},"citations":{"type":"array","items":{"$ref":"#/components/schemas/KnowledgeCitation"}}}},"PromptRunRequest":{"type":"object","properties":{"variables":{"type":"object","additionalProperties":{}},"knowledgeQuery":{"type":"string"},"requestId":{"type":"string"},"confirmedExternalProvider":{"type":"boolean"}}},"AiRoute":{"type":"object","properties":{"gatewayId":{"type":"string"},"modelId":{"type":"string"}}},"PromptCitation":{"type":"object","properties":{"chunkId":{"type":"string","format":"uuid"},"knowledgeAssetId":{"type":"string","format":"uuid"},"sourceRevisionId":{"type":"string","format":"uuid"},"title":{"type":"string"},"startPage":{"type":"integer","format":"int32"},"endPage":{"type":"integer","format":"int32"},"heading":{"type":"string"}}},"PromptRunResult":{"type":"object","properties":{"runId":{"type":"string","format":"uuid"},"assetId":{"type":"string","format":"uuid"},"releaseId":{"type":"string","format":"uuid"},"releaseDigest":{"type":"string"},"modelRoute":{"$ref":"#/components/schemas/AiRoute"},"output":{"type":"string"},"citations":{"type":"array","items":{"$ref":"#/components/schemas/PromptCitation"}},"durationMillis":{"type":"integer","format":"int64"}}},"PromptRunToolResult":{"type":"object","properties":{"traceId":{"type":"string","format":"uuid"},"result":{"$ref":"#/components/schemas/PromptRunResult"}}},"PromptRenderRequest":{"type":"object","properties":{"variables":{"type":"object","additionalProperties":{}}}},"PromptRenderResult":{"type":"object","properties":{"assetId":{"type":"string","format":"uuid"},"releaseId":{"type":"string","format":"uuid"},"releaseDigest":{"type":"string"},"systemInstruction":{"type":"string"},"userPrompt":{"type":"string"},"sensitiveVariables":{"type":"array","items":{"type":"string"}},"inputShapeDigest":{"type":"string"}}},"PromptRenderToolResult":{"type":"object","properties":{"traceId":{"type":"string","format":"uuid"},"result":{"$ref":"#/components/schemas/PromptRenderResult"}}},"ConfirmedActionRequest":{"type":"object","properties":{"confirmed":{"type":"boolean"}}},"ForkRequest":{"type":"object","properties":{"namespace":{"type":"string"},"slug":{"type":"string"},"knowledgeSpaceId":{"type":"string","format":"uuid"},"confirmed":{"type":"boolean"}}},"ForkResult":{"type":"object","properties":{"traceId":{"type":"string","format":"uuid"},"asset":{"$ref":"#/components/schemas/AssetView"}}},"FeedbackRequest":{"type":"object","properties":{"type":{"type":"string","enum":["HELPFUL","OUTDATED","INCORRECT","OTHER"]},"comment":{"type":"string"},"confirmed":{"type":"boolean"}}},"FeedbackResult":{"type":"object","properties":{"traceId":{"type":"string","format":"uuid"},"feedbackId":{"type":"string","format":"uuid"}}},"AssistantChatRequest":{"type":"object","properties":{"message":{"type":"string","maxLength":4000,"minLength":0},"limit":{"type":"integer","format":"int32"},"conversationId":{"type":"string","format":"uuid"}},"required":["message"]},"ServerSentEventString":{},"AssetDraftRequest":{"type":"object","properties":{"title":{"type":"string"},"summary":{"type":"string"},"classification":{"type":"string"},"schemaVersion":{"type":"string"},"payload":{"type":"string"}}},"CreateAssetRequest":{"type":"object","properties":{"type":{"type":"string","enum":["PROMPT_TEMPLATE","WORK_INSTRUCTION","CAPABILITY_PACK"]},"namespace":{"type":"string"},"slug":{"type":"string"},"knowledgeSpaceId":{"type":"string","format":"uuid"},"draft":{"$ref":"#/components/schemas/AssetDraftRequest"}}},"SubmitAssetRevisionRequest":{"type":"object","properties":{"changeNote":{"type":"string"}}},"AssignAssetRoleRequest":{"type":"object","properties":{"principalType":{"type":"string"},"principalId":{"type":"string"},"role":{"type":"string","enum":["OWNER","BACKUP_OWNER","STEWARD","VIEWER","EDITOR","REVIEWER","PUBLISHER"]}}},"AssetReviewDecisionRequest":{"type":"object","properties":{"decision":{"type":"string","enum":["REQUEST_CHANGES","REJECT","APPROVE","CANCEL"]},"comment":{"type":"string"}}},"PublishAssetReleaseRequest":{"type":"object","properties":{"revisionId":{"type":"string","format":"uuid"},"versionLabel":{"type":"string"}}},"Step":{"type":"object","properties":{"key":{"type":"string"},"title":{"type":"string"},"instruction":{"type":"string"},"expectedResult":{"type":"string"},"check":{"type":"string"},"escalation":{"type":"string"},"prohibitedActions":{"type":"array","items":{"type":"string"}},"relatedAssetIds":{"type":"array","items":{"type":"string","format":"uuid"}},"relatedKnowledgeVersionIds":{"type":"array","items":{"type":"string","format":"uuid"}}}},"WorkInstructionSpec":{"type":"object","properties":{"purpose":{"type":"string"},"audience":{"type":"string"},"prerequisites":{"type":"array","items":{"type":"string"}},"completionOutcome":{"type":"string"},"responsibleRole":{"type":"string"},"steps":{"type":"array","items":{"$ref":"#/components/schemas/Step"}}}},"WorkInstructionView":{"type":"object","properties":{"assetId":{"type":"string","format":"uuid"},"releaseId":{"type":"string","format":"uuid"},"releaseDigest":{"type":"string"},"title":{"type":"string"},"versionLabel":{"type":"string"},"instruction":{"$ref":"#/components/schemas/WorkInstructionSpec"},"acknowledged":{"type":"boolean"},"acknowledgedAt":{"type":"string","format":"date-time"}}},"AssetAvailabilityRequest":{"type":"object","properties":{"reason":{"type":"string"}}},"PromptVariablesRequest":{"type":"object","properties":{"variables":{"type":"object","additionalProperties":{}}}},"CaseResult":{"type":"object","properties":{"name":{"type":"string"},"passed":{"type":"boolean"},"failedAssertions":{"type":"array","items":{"type":"string"}},"promptRunId":{"type":"string","format":"uuid"}}},"PromptEvaluationResult":{"type":"object","properties":{"evaluationId":{"type":"string","format":"uuid"},"assetId":{"type":"string","format":"uuid"},"releaseId":{"type":"string","format":"uuid"},"releaseDigest":{"type":"string"},"passedCases":{"type":"integer","format":"int32"},"totalCases":{"type":"integer","format":"int32"},"cases":{"type":"array","items":{"$ref":"#/components/schemas/CaseResult"}}}},"ForkReleaseRequest":{"type":"object","properties":{"namespace":{"type":"string"},"slug":{"type":"string"},"knowledgeSpaceId":{"type":"string","format":"uuid"}}},"PromptComparisonRequest":{"type":"object","properties":{"baselineReleaseId":{"type":"string","format":"uuid"},"candidateReleaseId":{"type":"string","format":"uuid"}}},"PromptEvaluationComparison":{"type":"object","properties":{"baseline":{"$ref":"#/components/schemas/PromptEvaluationResult"},"candidate":{"$ref":"#/components/schemas/PromptEvaluationResult"},"passedCaseDelta":{"type":"integer","format":"int32"}}},"AssignRoleRequest":{"type":"object","properties":{"userId":{"type":"string","format":"uuid"}}},"CreateKnowledgeSpaceRequest":{"type":"object","properties":{"name":{"type":"string"},"departmentId":{"type":"string","format":"uuid"}}},"AdminKnowledgeSpaceResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"key":{"type":"string"},"name":{"type":"string"},"departmentId":{"type":"string","format":"uuid"},"active":{"type":"boolean"},"grants":{"type":"array","items":{"$ref":"#/components/schemas/KnowledgeSpaceGrantResponse"}},"grantsComplete":{"type":"boolean"},"policyVersion":{"type":"string"}}},"KnowledgeSpaceGrantResponse":{"type":"object","properties":{"relation":{"type":"string"},"subject":{"type":"string"}}},"GrantKnowledgeSpaceAccessRequest":{"type":"object","properties":{"relation":{"type":"string"},"kind":{"type":"string","enum":["ORGANIZATION","DEPARTMENT","DEPARTMENT_MANAGERS","ROLE","USER"]},"subjectId":{"type":"string","format":"uuid"},"role":{"type":"string"}}},"CreateInvitationRequest":{"type":"object","properties":{"email":{"type":"string"},"role":{"type":"string","enum":["EMPLOYEE","TEAM_LEAD","MANAGER","DIRECTOR","EXECUTIVE","ADMIN"]},"departmentId":{"type":"string","format":"uuid"}}},"AdminInvitationResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"email":{"type":"string"},"role":{"type":"string","enum":["EMPLOYEE","TEAM_LEAD","MANAGER","DIRECTOR","EXECUTIVE","ADMIN"]},"departmentId":{"type":"string","format":"uuid"},"status":{"type":"string"},"invitedAt":{"type":"string","format":"date-time"},"acceptedAt":{"type":"string","format":"date-time"},"acceptedAppUserId":{"type":"string","format":"uuid"}}},"AdminConnectorProbeResponse":{"type":"object","properties":{"authenticated":{"type":"boolean"},"connectionKey":{"type":"string"},"accountName":{"type":"string"},"identityName":{"type":"string"},"canReadContent":{"type":"boolean"},"errorCode":{"type":"string"}}},"ExplainAccessRequest":{"type":"object","properties":{"userId":{"type":"string","format":"uuid"},"permission":{"type":"string"},"resourceType":{"type":"string"},"resourceId":{"type":"string","format":"uuid"}}},"AccessBlockResponse":{"type":"object","properties":{"branch":{"type":"string"},"kind":{"type":"string"},"detail":{"type":"string"}}},"AccessStepResponse":{"type":"object","properties":{"object":{"type":"string"},"relation":{"type":"string"},"kind":{"type":"string"}}},"AclProvenanceResponse":{"type":"object","properties":{"authority":{"type":"string"},"origin":{"type":"string"},"generation":{"type":"integer","format":"int64"},"capturedAt":{"type":"string","format":"date-time"},"expired":{"type":"boolean"}}},"ExplainAccessResponse":{"type":"object","properties":{"state":{"type":"string","enum":["ALLOWED","DENIED","UNKNOWN"]},"reasonCode":{"type":"string"},"path":{"type":"array","items":{"$ref":"#/components/schemas/AccessStepResponse"}},"blockedBy":{"type":"array","items":{"$ref":"#/components/schemas/AccessBlockResponse"}},"provenance":{"$ref":"#/components/schemas/AclProvenanceResponse"},"policyVersion":{"type":"string"},"evaluatedAt":{"type":"string","format":"date-time"}}},"RenameConversationRequest":{"type":"object","properties":{"title":{"type":"string","maxLength":120,"minLength":0}},"required":["title"]},"UpdateAdminUserRequest":{"type":"object","properties":{"role":{"type":"string","enum":["EMPLOYEE","TEAM_LEAD","MANAGER","DIRECTOR","EXECUTIVE","ADMIN"]},"active":{"type":"boolean"}}},"AdminUserResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"email":{"type":"string"},"role":{"type":"string","enum":["EMPLOYEE","TEAM_LEAD","MANAGER","DIRECTOR","EXECUTIVE","ADMIN"]},"departmentId":{"type":"string","format":"uuid"},"active":{"type":"boolean"},"signInLinked":{"type":"boolean"},"mappedPrincipalCount":{"type":"integer","format":"int32"}}},"SessionResponse":{"type":"object","properties":{"authenticated":{"type":"boolean"},"name":{"type":"string"},"email":{"type":"string"},"userId":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"departmentId":{"type":"string","format":"uuid"},"role":{"type":"string","enum":["EMPLOYEE","TEAM_LEAD","MANAGER","DIRECTOR","EXECUTIVE","ADMIN"]}}},"CsrfResponse":{"type":"object","properties":{"headerName":{"type":"string"},"parameterName":{"type":"string"},"token":{"type":"string"}}},"DepartmentResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"name":{"type":"string"}}},"OrganizationContextResponse":{"type":"object","properties":{"organizationId":{"type":"string","format":"uuid"},"departments":{"type":"array","items":{"$ref":"#/components/schemas/DepartmentResponse"}},"users":{"type":"array","items":{"$ref":"#/components/schemas/UserResponse"}}}},"UserResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"departmentId":{"type":"string","format":"uuid"},"name":{"type":"string"},"email":{"type":"string"},"role":{"type":"string","enum":["EMPLOYEE","TEAM_LEAD","MANAGER","DIRECTOR","EXECUTIVE","ADMIN"]}}},"MeResponse":{"type":"object","properties":{"authenticated":{"type":"boolean"},"subject":{"type":"string"},"email":{"type":"string"},"name":{"type":"string"},"authorizationProvider":{"type":"string"},"userId":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"departmentId":{"type":"string","format":"uuid"},"role":{"type":"string","enum":["EMPLOYEE","TEAM_LEAD","MANAGER","DIRECTOR","EXECUTIVE","ADMIN"]}}},"KnowledgeEvidenceResponse":{"type":"object","properties":{"citationId":{"type":"string","format":"uuid"},"knowledgeAssetId":{"type":"string","format":"uuid"},"title":{"type":"string"},"content":{"type":"string"},"sourceUri":{"type":"string"},"startPage":{"type":"integer","format":"int32"},"endPage":{"type":"integer","format":"int32"},"heading":{"type":"string"},"relevanceScore":{"type":"number","format":"double"}}},"KnowledgeSearchResponse":{"type":"object","properties":{"requestId":{"type":"string"},"evidence":{"type":"array","items":{"$ref":"#/components/schemas/KnowledgeEvidenceResponse"}}}},"KnowledgeCatalogItem":{"type":"object","properties":{"knowledgeAssetId":{"type":"string","format":"uuid"},"knowledgeVersionId":{"type":"string","format":"uuid"},"versionNumber":{"type":"integer","format":"int64"},"knowledgeSpaceId":{"type":"string","format":"uuid"},"title":{"type":"string"},"language":{"type":"string"},"classification":{"type":"string","enum":["PUBLIC","INTERNAL","CONFIDENTIAL","RESTRICTED"]},"contentDigest":{"type":"string"}}},"Entity":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"type":{"type":"string"},"description":{"type":"string"},"citationChunkIds":{"type":"array","items":{"type":"string","format":"uuid"}},"governingEvidence":{"$ref":"#/components/schemas/EvidenceReference"}}},"KnowledgeGraphView":{"type":"object","properties":{"knowledgeSpaceId":{"type":"string","format":"uuid"},"authorizationGeneration":{"type":"integer","format":"int64"},"canCurate":{"type":"boolean"},"entities":{"type":"array","items":{"$ref":"#/components/schemas/Entity"}},"relations":{"type":"array","items":{"$ref":"#/components/schemas/Relation"}},"truncated":{"type":"boolean"}}},"Relation":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"sourceEntityId":{"type":"string","format":"uuid"},"targetEntityId":{"type":"string","format":"uuid"},"type":{"type":"string"},"description":{"type":"string"},"weight":{"type":"number","format":"double"},"keywords":{"type":"array","items":{"type":"string"}},"citationChunkIds":{"type":"array","items":{"type":"string","format":"uuid"}},"governingEvidence":{"$ref":"#/components/schemas/EvidenceReference"}}},"KnowledgeSpaceResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"key":{"type":"string"},"name":{"type":"string"},"departmentId":{"type":"string","format":"uuid"}}},"StreamingResponseBody":{},"WorkInstructionToolResult":{"type":"object","properties":{"traceId":{"type":"string","format":"uuid"},"instruction":{"$ref":"#/components/schemas/WorkInstructionView"}}},"AssistantReleaseRef":{"type":"object","properties":{"assetId":{"type":"string","format":"uuid"},"releaseId":{"type":"string","format":"uuid"},"releaseDigest":{"type":"string"}}},"PromptFormResult":{"type":"object","properties":{"traceId":{"type":"string","format":"uuid"},"release":{"$ref":"#/components/schemas/AssistantReleaseRef"},"objective":{"type":"string"},"audience":{"type":"string"},"variables":{"type":"array","items":{"$ref":"#/components/schemas/Variable"}},"outputContract":{"type":"object","additionalProperties":{}},"knowledgeRequirements":{"type":"array","items":{"type":"string"}},"knownLimitations":{"type":"string"}}},"Variable":{"type":"object","properties":{"name":{"type":"string"},"type":{"type":"string","enum":["STRING","INTEGER","NUMBER","BOOLEAN","STRING_LIST"]},"required":{"type":"boolean"},"defaultValue":{},"sensitive":{"type":"boolean"},"pattern":{"type":"string"},"allowedValues":{"type":"array","items":{"type":"string"}}}},"AssetRecommendation":{"type":"object","properties":{"assetId":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["PROMPT_TEMPLATE","WORK_INSTRUCTION","CAPABILITY_PACK"]},"namespace":{"type":"string"},"slug":{"type":"string"},"title":{"type":"string"},"summary":{"type":"string"},"knowledgeSpaceId":{"type":"string","format":"uuid"},"portfolioState":{"type":"string","enum":["DRAFT_ONLY","ACTIVE","SUNSETTING","RETIRED"]},"releaseId":{"type":"string","format":"uuid"},"versionLabel":{"type":"string"},"releaseDigest":{"type":"string"},"availability":{"type":"string","enum":["AVAILABLE","DEPRECATED","WITHDRAWN"]}}},"RecommendationResult":{"type":"object","properties":{"traceId":{"type":"string","format":"uuid"},"recommendations":{"type":"array","items":{"$ref":"#/components/schemas/AssetRecommendation"}}}},"AssistantConversationSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"lastActivityAt":{"type":"string","format":"date-time"},"messageCount":{"type":"integer","format":"int64"}}},"AssistantConversationMessageView":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"role":{"type":"string","enum":["USER","ASSISTANT"]},"content":{"type":"string"},"sequence":{"type":"integer","format":"int64"},"occurredAt":{"type":"string","format":"date-time"}}},"AssetSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["PROMPT_TEMPLATE","WORK_INSTRUCTION","CAPABILITY_PACK"]},"namespace":{"type":"string"},"slug":{"type":"string"},"title":{"type":"string"},"summary":{"type":"string"},"knowledgeSpaceId":{"type":"string","format":"uuid"},"portfolioState":{"type":"string","enum":["DRAFT_ONLY","ACTIVE","SUNSETTING","RETIRED"]}}},"AssetDeliveryRelease":{"type":"object","properties":{"assetId":{"type":"string","format":"uuid"},"releaseId":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["PROMPT_TEMPLATE","WORK_INSTRUCTION","CAPABILITY_PACK"]},"namespace":{"type":"string"},"slug":{"type":"string"},"versionLabel":{"type":"string"},"title":{"type":"string"},"summary":{"type":"string"},"classification":{"type":"string"},"schemaVersion":{"type":"string"},"payload":{"type":"string"},"digest":{"type":"string"},"availability":{"type":"string","enum":["AVAILABLE","DEPRECATED","WITHDRAWN"]},"releasedAt":{"type":"string","format":"date-time"}}},"AssetRelationResolution":{"type":"object","properties":{"assetId":{"type":"string","format":"uuid"},"releaseId":{"type":"string","format":"uuid"},"accessGap":{"type":"boolean"},"relations":{"type":"array","items":{"$ref":"#/components/schemas/Relation"}}}},"CapabilityPackDefinition":{"type":"object","properties":{"packAssetId":{"type":"string","format":"uuid"},"packReleaseId":{"type":"string","format":"uuid"},"releaseDigest":{"type":"string"},"title":{"type":"string"},"versionLabel":{"type":"string"},"purpose":{"type":"string","enum":["ROLE_ONBOARDING","HANDOVER","ROLE_ENABLEMENT"]},"audience":{"type":"string"},"prerequisites":{"type":"array","items":{"type":"string"}},"expectedOutcome":{"type":"string"},"completionCriteria":{"type":"array","items":{"type":"string"}},"reviewDate":{"type":"string"},"owner":{"type":"string"},"accessGap":{"type":"boolean"},"items":{"type":"array","items":{"$ref":"#/components/schemas/Item"}}}},"EffectivePermissionResponse":{"type":"object","properties":{"userId":{"type":"string","format":"uuid"},"permissions":{"type":"object","additionalProperties":{"type":"string","enum":["ALLOWED","DENIED","UNKNOWN"]}},"evaluatedAt":{"type":"string","format":"date-time"}}},"AdminSourceGroupMemberResponse":{"type":"object","properties":{"principalId":{"type":"string","format":"uuid"},"externalKey":{"type":"string"},"observedDisplayName":{"type":"string"},"observedEmail":{"type":"string"},"appUserId":{"type":"string","format":"uuid"},"appUserName":{"type":"string"}}},"AdminSourceGroupResponse":{"type":"object","properties":{"principalId":{"type":"string","format":"uuid"},"sourceSystem":{"type":"string"},"sourceConnectionKey":{"type":"string"},"externalKey":{"type":"string"},"observedDisplayName":{"type":"string"},"sourceAclSnapshotId":{"type":"string","format":"uuid"},"aclGeneration":{"type":"integer","format":"int64"},"sealedAt":{"type":"string","format":"date-time"},"members":{"type":"array","items":{"$ref":"#/components/schemas/AdminSourceGroupMemberResponse"}}}},"AdminRoleListResponse":{"type":"object","properties":{"roles":{"type":"array","items":{"$ref":"#/components/schemas/AdminRoleResponse"}},"complete":{"type":"boolean"},"policyVersion":{"type":"string"}}},"AdminRoleResponse":{"type":"object","properties":{"role":{"type":"string"},"assignees":{"type":"array","items":{"type":"string"}}}},"KnowledgeSpaceGrantOptionResponse":{"type":"object","properties":{"relation":{"type":"string"},"kinds":{"type":"array","items":{"type":"string","enum":["ORGANIZATION","DEPARTMENT","DEPARTMENT_MANAGERS","ROLE","USER"]}}}},"AdminConnectorScopeResponse":{"type":"object","properties":{"key":{"type":"string"},"displayName":{"type":"string"},"reachable":{"type":"boolean"},"admissible":{"type":"boolean"},"instruction":{"type":"string"}}},"AdminConnectionActivityResponse":{"type":"object","properties":{"sourceSystem":{"type":"string"},"sourceConnectionKey":{"type":"string"},"objectsTotal":{"type":"integer","format":"int64"},"objectsActive":{"type":"integer","format":"int64"},"objectsArchived":{"type":"integer","format":"int64"},"lastObjectAt":{"type":"string","format":"date-time"},"lastCrawlAt":{"type":"string","format":"date-time"},"recentAttempts":{"type":"array","items":{"$ref":"#/components/schemas/AdminCrawlAttemptResponse"}}}},"AdminCrawlAttemptResponse":{"type":"object","properties":{"outcome":{"type":"string"},"objectsMaterialized":{"type":"integer","format":"int32"},"objectsRotated":{"type":"integer","format":"int32"},"objectsRematerialized":{"type":"integer","format":"int32"},"objectsRetired":{"type":"integer","format":"int32"},"objectsFailed":{"type":"integer","format":"int32"},"errorCode":{"type":"string"},"errorMessage":{"type":"string"},"attemptedAt":{"type":"string","format":"date-time"}}},"AdminConnectorSourceResponse":{"type":"object","properties":{"sourceSystem":{"type":"string"},"displayName":{"type":"string"}}},"KnowledgeAssetRef":{"type":"object","properties":{"knowledgeAssetId":{"type":"string","format":"uuid"},"knowledgeAssetVersionId":{"type":"string","format":"uuid"},"normalizedRecordId":{"type":"string","format":"uuid"},"rawSourceObjectId":{"type":"string","format":"uuid"},"sourceAclSnapshotId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["PENDING","ACTIVE","RETIRED"]}}}}}} \ No newline at end of file +{"openapi":"3.1.0","info":{"title":"OpenAPI definition","version":"v0"},"servers":[{"url":"http://127.0.0.1:8080","description":"Generated server url"}],"paths":{"/api/assistant/tools/assets/{assetId}/releases/{releaseId}/pack/{itemKey}":{"put":{"tags":["assistant-asset-tool-controller"],"summary":"Update actor-derived Pack progress after explicit confirmation","operationId":"updateAssistantPackProgress","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"itemKey","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PackProgressRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PackToolResult"}}}}}}},"/api/assets/{assetId}/releases/{releaseId}/pack-progress/{itemKey}":{"put":{"tags":["asset-consumption-controller"],"summary":"Idempotently update actor-derived progress for one accessible Pack item","operationId":"setCapabilityPackProgress","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"itemKey","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PackProgressRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PackJourney"}}}}}}},"/api/assets/{assetId}/draft":{"put":{"tags":["asset-registry-controller"],"summary":"Update a mutable Asset draft","operationId":"updateAssetDraft","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAssetDraftRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AssetView"}}}}}}},"/api/admin/source-principals/{principalId}/mapping":{"put":{"tags":["admin-source-access-controller"],"summary":"Confirm a principal maps to an internal user","operationId":"confirmAdminSourceMapping","parameters":[{"name":"principalId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmMappingRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AdminSourcePrincipalResponse"}}}}}},"delete":{"tags":["admin-source-access-controller"],"summary":"Revoke a principal's active mapping","operationId":"revokeAdminSourceMapping","parameters":[{"name":"principalId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AdminSourcePrincipalResponse"}}}}}}},"/api/admin/source-connections/identity-trust":{"put":{"tags":["admin-source-access-controller"],"summary":"Record the identity trust for a connection","operationId":"setAdminSourceConnectionTrust","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdentityTrustRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AdminSourceConnectionResponse"}}}}}}},"/api/admin/connectors/{sourceSystem}/{connectionKey}":{"put":{"tags":["admin-connector-controller"],"summary":"Record how a connection is crawled","operationId":"configureAdminConnection","parameters":[{"name":"sourceSystem","in":"path","required":true,"schema":{"type":"string"}},{"name":"connectionKey","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfigureConnectionRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AdminConnectionResponse"}}}}}}},"/api/admin/connectors/{sourceSystem}/{connectionKey}/credential":{"put":{"tags":["admin-connector-controller"],"summary":"Store a credential for a connection","operationId":"setAdminConnectionCredential","parameters":[{"name":"sourceSystem","in":"path","required":true,"schema":{"type":"string"}},{"name":"connectionKey","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectorCredentialRequest"}}},"required":true},"responses":{"204":{"description":"No Content"}}},"delete":{"tags":["admin-connector-controller"],"summary":"Forget a connection's stored credential","operationId":"forgetAdminConnectionCredential","parameters":[{"name":"sourceSystem","in":"path","required":true,"schema":{"type":"string"}},{"name":"connectionKey","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"No Content"}}}},"/api/sources":{"get":{"tags":["source-controller"],"summary":"List sources visible to the current user","operationId":"listSources","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SourceResponse"}}}}}}},"post":{"tags":["source-controller"],"summary":"Upload a source for asynchronous ingestion","operationId":"uploadSource","parameters":[{"name":"classification","in":"query","required":false,"schema":{"type":"string","default":"CONFIDENTIAL","enum":["PUBLIC","INTERNAL","CONFIDENTIAL","RESTRICTED"]}},{"name":"knowledgeSpaceId","in":"query","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"}},"required":["file"]}}}},"responses":{"201":{"description":"Created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SourceResponse"}}}}}}},"/api/knowledge-spaces/{knowledgeSpaceId}/graph/curations/suppressions":{"post":{"tags":["knowledge-graph-management-controller"],"summary":"Delete an effective graph identity without deleting evidence","operationId":"suppressGraphIdentity","parameters":[{"name":"knowledgeSpaceId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuppressIdentityRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"oneOf":[{"$ref":"#/components/schemas/CuratedEntity"},{"$ref":"#/components/schemas/CuratedRelation"},{"$ref":"#/components/schemas/IdentityAlias"},{"$ref":"#/components/schemas/IdentitySuppression"}]}}}}}}},"/api/knowledge-spaces/{knowledgeSpaceId}/graph/curations/relations":{"post":{"tags":["knowledge-graph-management-controller"],"summary":"Create or edit a governed graph relation","operationId":"curateGraphRelation","parameters":[{"name":"knowledgeSpaceId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CurateRelationRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"oneOf":[{"$ref":"#/components/schemas/CuratedEntity"},{"$ref":"#/components/schemas/CuratedRelation"},{"$ref":"#/components/schemas/IdentityAlias"},{"$ref":"#/components/schemas/IdentitySuppression"}]}}}}}}},"/api/knowledge-spaces/{knowledgeSpaceId}/graph/curations/entities":{"post":{"tags":["knowledge-graph-management-controller"],"summary":"Create or edit a governed graph entity","operationId":"curateGraphEntity","parameters":[{"name":"knowledgeSpaceId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CurateEntityRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"oneOf":[{"$ref":"#/components/schemas/CuratedEntity"},{"$ref":"#/components/schemas/CuratedRelation"},{"$ref":"#/components/schemas/IdentityAlias"},{"$ref":"#/components/schemas/IdentitySuppression"}]}}}}}}},"/api/knowledge-spaces/{knowledgeSpaceId}/graph/curations/aliases":{"post":{"tags":["knowledge-graph-management-controller"],"summary":"Merge graph identities through a reversible alias","operationId":"mergeGraphIdentity","parameters":[{"name":"knowledgeSpaceId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AliasIdentityRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"oneOf":[{"$ref":"#/components/schemas/CuratedEntity"},{"$ref":"#/components/schemas/CuratedRelation"},{"$ref":"#/components/schemas/IdentityAlias"},{"$ref":"#/components/schemas/IdentitySuppression"}]}}}}}}},"/api/knowledge-assets/{knowledgeAssetId}/graph-index":{"post":{"tags":["knowledge-asset-lifecycle-controller"],"summary":"Ensure graph indexing uses the current processing profile","operationId":"ensureKnowledgeAssetGraphIndex","parameters":[{"name":"knowledgeAssetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"202":{"description":"Accepted","content":{"*/*":{"schema":{"$ref":"#/components/schemas/GraphIndexJobView"}}}}}}},"/api/knowledge-assets/graph-jobs/{jobId}/resume":{"post":{"tags":["knowledge-asset-lifecycle-controller"],"summary":"Resume unfinished graph indexing","operationId":"resumeGraphIndexJob","parameters":[{"name":"jobId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"202":{"description":"Accepted","content":{"*/*":{"schema":{"$ref":"#/components/schemas/GraphIndexJobView"}}}}}}},"/api/knowledge-assets/graph-jobs/{jobId}/cancel":{"post":{"tags":["knowledge-asset-lifecycle-controller"],"summary":"Cancel queued or in-flight graph indexing","operationId":"cancelGraphIndexJob","parameters":[{"name":"jobId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/GraphIndexJobView"}}}}}}},"/api/assistant/tools/knowledge-search":{"post":{"tags":["assistant-asset-tool-controller"],"summary":"Search canonical permission-aware Knowledge and return citation references","operationId":"searchAssistantKnowledge","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeSearchRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/KnowledgeResult"}}}}}}},"/api/assistant/tools/assets/{assetId}/releases/{releaseId}/prompt-run":{"post":{"tags":["assistant-asset-tool-controller"],"summary":"Run an exact Prompt release after explicit external-provider confirmation","operationId":"runAssistantPrompt","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptRunRequest"}}},"required":true},"responses":{"201":{"description":"Created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PromptRunToolResult"}}}}}}},"/api/assistant/tools/assets/{assetId}/releases/{releaseId}/prompt-render":{"post":{"tags":["assistant-asset-tool-controller"],"summary":"Render an exact Prompt release after variable validation","operationId":"renderAssistantPrompt","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptRenderRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PromptRenderToolResult"}}}}}}},"/api/assistant/tools/assets/{assetId}/releases/{releaseId}/pack":{"get":{"tags":["assistant-asset-tool-controller"],"summary":"Read an actor-scoped exact Pack journey","operationId":"readAssistantPack","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PackToolResult"}}}}}},"post":{"tags":["assistant-asset-tool-controller"],"summary":"Start an exact Pack after explicit state-change confirmation","operationId":"startAssistantPack","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmedActionRequest"}}},"required":true},"responses":{"201":{"description":"Created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PackToolResult"}}}}}}},"/api/assistant/tools/assets/{assetId}/releases/{releaseId}/fork":{"post":{"tags":["assistant-asset-tool-controller"],"summary":"Fork an exact release after explicit draft-creation confirmation","operationId":"forkAssistantAssetRelease","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForkRequest"}}},"required":true},"responses":{"201":{"description":"Created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ForkResult"}}}}}}},"/api/assistant/tools/assets/{assetId}/releases/{releaseId}/feedback":{"post":{"tags":["assistant-asset-tool-controller"],"summary":"Submit feedback against an exact release after explicit confirmation","operationId":"submitAssistantAssetFeedback","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackRequest"}}},"required":true},"responses":{"201":{"description":"Created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/FeedbackResult"}}}}}}},"/api/assistant/chat":{"post":{"tags":["assistant-controller"],"summary":"Stream an answer from permission-verified knowledge","operationId":"streamAssistantChat","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssistantChatRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"text/event-stream":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ServerSentEventString"}}}}}}}},"/api/assets":{"get":{"tags":["asset-registry-controller"],"summary":"List released or authoring Assets visible to the actor","operationId":"listAssets","parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string"}},{"name":"type","in":"query","required":false,"schema":{"type":"string","enum":["PROMPT_TEMPLATE","WORK_INSTRUCTION","CAPABILITY_PACK"]}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AssetSummary"}}}}}}},"post":{"tags":["asset-registry-controller"],"summary":"Create an Asset and its mutable draft","operationId":"createAsset","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAssetRequest"}}},"required":true},"responses":{"201":{"description":"Created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AssetView"}}}}}}},"/api/assets/{assetId}/submissions":{"post":{"tags":["asset-registry-controller"],"summary":"Submit an immutable Asset revision for review","operationId":"submitAssetRevision","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitAssetRevisionRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AssetView"}}}}}}},"/api/assets/{assetId}/role-assignments":{"post":{"tags":["asset-registry-controller"],"summary":"Assign an accountable role on an Asset","operationId":"assignAssetRole","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssignAssetRoleRequest"}}},"required":true},"responses":{"201":{"description":"Created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AssetView"}}}}}}},"/api/assets/{assetId}/reviews/{reviewCaseId}/decisions":{"post":{"tags":["asset-registry-controller"],"summary":"Record a decision against an exact revision digest","operationId":"decideAssetReview","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"reviewCaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetReviewDecisionRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AssetView"}}}}}}},"/api/assets/{assetId}/releases":{"post":{"tags":["asset-registry-controller"],"summary":"Publish an approved immutable Asset revision","operationId":"publishAssetRelease","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublishAssetReleaseRequest"}}},"required":true},"responses":{"201":{"description":"Created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AssetView"}}}}}}},"/api/assets/{assetId}/releases/{releaseId}/work-instruction/acknowledgement":{"post":{"tags":["asset-consumption-controller"],"summary":"Idempotently acknowledge an exact Work Instruction release","operationId":"acknowledgeWorkInstruction","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/WorkInstructionView"}}}}}}},"/api/assets/{assetId}/releases/{releaseId}/withdrawal":{"post":{"tags":["asset-registry-controller"],"summary":"Withdraw an Asset release from new use","operationId":"withdrawAssetRelease","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetAvailabilityRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AssetView"}}}}}}},"/api/assets/{assetId}/releases/{releaseId}/prompt/runs":{"post":{"tags":["asset-consumption-controller"],"summary":"Run an exact Prompt release through the provider-neutral AI gateway","operationId":"runPromptRelease","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptRunRequest"}}},"required":true},"responses":{"201":{"description":"Created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PromptRunResult"}}}}}}},"/api/assets/{assetId}/releases/{releaseId}/prompt/render":{"post":{"tags":["asset-consumption-controller"],"summary":"Deterministically render an exact authorized Prompt release","operationId":"renderPromptRelease","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptVariablesRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PromptRenderResult"}}}}}}},"/api/assets/{assetId}/releases/{releaseId}/prompt/evaluations":{"post":{"tags":["asset-consumption-controller"],"summary":"Run the bounded evaluation cases pinned in a Prompt release","operationId":"evaluatePromptRelease","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"201":{"description":"Created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PromptEvaluationResult"}}}}}}},"/api/assets/{assetId}/releases/{releaseId}/pack-assignment":{"post":{"tags":["asset-consumption-controller"],"summary":"Start or resume an exact authorized Capability Pack release","operationId":"startCapabilityPack","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"201":{"description":"Created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PackJourney"}}}}}}},"/api/assets/{assetId}/releases/{releaseId}/forks":{"post":{"tags":["asset-consumption-controller"],"summary":"Fork an exact authorized release into a new mutable draft","operationId":"forkAssetRelease","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForkReleaseRequest"}}},"required":true},"responses":{"201":{"description":"Created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AssetView"}}}}}}},"/api/assets/{assetId}/releases/{releaseId}/deprecation":{"post":{"tags":["asset-registry-controller"],"summary":"Deprecate an Asset release","operationId":"deprecateAssetRelease","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetAvailabilityRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AssetView"}}}}}}},"/api/assets/{assetId}/prompt/evaluation-comparisons":{"post":{"tags":["asset-consumption-controller"],"summary":"Compare bounded evaluation results for two exact Prompt releases","operationId":"comparePromptReleases","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptComparisonRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PromptEvaluationComparison"}}}}}}},"/api/asset-delivery/{assetId}/releases/{releaseId}/prompt-render":{"post":{"tags":["asset-delivery-controller"],"summary":"Deterministically render an exact authorized Prompt release","operationId":"renderReleasedPrompt","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptVariablesRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PromptRenderResult"}}}}}}},"/api/admin/roles/{role}/members":{"post":{"tags":["admin-role-controller"],"summary":"Assign a user to a role","operationId":"assignAdminRole","parameters":[{"name":"role","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssignRoleRequest"}}},"required":true},"responses":{"204":{"description":"No Content"}}}},"/api/admin/knowledge-spaces":{"get":{"tags":["admin-knowledge-space-controller"],"summary":"List Knowledge Spaces and the grants stored against them","operationId":"listAdminKnowledgeSpaces","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AdminKnowledgeSpaceResponse"}}}}}}},"post":{"tags":["admin-knowledge-space-controller"],"summary":"Create a Knowledge Space","operationId":"createAdminKnowledgeSpace","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateKnowledgeSpaceRequest"}}},"required":true},"responses":{"201":{"description":"Created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AdminKnowledgeSpaceResponse"}}}}}}},"/api/admin/knowledge-spaces/{knowledgeSpaceId}/grants":{"post":{"tags":["admin-knowledge-space-controller"],"summary":"Grant a subject access to a Knowledge Space","operationId":"grantAdminKnowledgeSpaceAccess","parameters":[{"name":"knowledgeSpaceId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantKnowledgeSpaceAccessRequest"}}},"required":true},"responses":{"204":{"description":"No Content"}}},"delete":{"tags":["admin-knowledge-space-controller"],"summary":"Revoke a subject's access to a Knowledge Space","operationId":"revokeAdminKnowledgeSpaceAccess","parameters":[{"name":"knowledgeSpaceId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"relation","in":"query","required":true,"schema":{"type":"string"}},{"name":"kind","in":"query","required":true,"schema":{"type":"string","enum":["ORGANIZATION","DEPARTMENT","DEPARTMENT_MANAGERS","ROLE","USER"]}},{"name":"subjectId","in":"query","required":false,"schema":{"type":"string","format":"uuid"}},{"name":"role","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"204":{"description":"No Content"}}}},"/api/admin/invitations":{"get":{"tags":["admin-invitation-controller"],"summary":"List invited addresses and their status","operationId":"listAdminInvitations","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AdminInvitationResponse"}}}}}}},"post":{"tags":["admin-invitation-controller"],"summary":"Expect an address to sign in","operationId":"createAdminInvitation","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateInvitationRequest"}}},"required":true},"responses":{"201":{"description":"Created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AdminInvitationResponse"}}}}}}},"/api/admin/connectors/{sourceSystem}/{connectionKey}/test":{"post":{"tags":["admin-connector-controller"],"summary":"Check a connection's stored credential","operationId":"testAdminConnection","parameters":[{"name":"sourceSystem","in":"path","required":true,"schema":{"type":"string"}},{"name":"connectionKey","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AdminConnectorProbeResponse"}}}}}}},"/api/admin/connectors/{sourceSystem}/{connectionKey}/crawl":{"post":{"tags":["admin-connector-controller"],"summary":"Ask for a content crawl on the next poll","operationId":"requestAdminConnectionCrawl","parameters":[{"name":"sourceSystem","in":"path","required":true,"schema":{"type":"string"}},{"name":"connectionKey","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"202":{"description":"Accepted"}}}},"/api/admin/connectors/{sourceSystem}/test":{"post":{"tags":["admin-connector-controller"],"summary":"Check a credential without storing it","operationId":"testAdminConnectorCredential","parameters":[{"name":"sourceSystem","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectorCredentialRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AdminConnectorProbeResponse"}}}}}}},"/api/admin/access/explain":{"post":{"tags":["admin-permission-controller"],"summary":"Answer whether a user holds a permission on one resource, and by which derivation","operationId":"explainAdminAccess","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExplainAccessRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ExplainAccessResponse"}}}}}}},"/api/assistant/conversations/{conversationId}":{"delete":{"tags":["assistant-controller"],"summary":"Delete the current actor's transcript and bounded model memory","operationId":"deleteAssistantConversation","parameters":[{"name":"conversationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"No Content"}}},"patch":{"tags":["assistant-controller"],"summary":"Rename the current actor's conversation","operationId":"renameAssistantConversation","parameters":[{"name":"conversationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenameConversationRequest"}}},"required":true},"responses":{"204":{"description":"No Content"}}}},"/api/admin/users/{userId}":{"patch":{"tags":["admin-user-controller"],"summary":"Change a user's role or activation","operationId":"updateAdminUser","parameters":[{"name":"userId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAdminUserRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AdminUserResponse"}}}}}}},"/api/session":{"get":{"tags":["browser-session-controller"],"summary":"Read the current browser session","operationId":"getBrowserSession","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SessionResponse"}}}}}}},"/api/session/csrf":{"get":{"tags":["browser-session-controller"],"summary":"Issue a CSRF token for browser mutations","operationId":"getBrowserCsrfToken","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CsrfResponse"}}}}}}},"/api/organization/context":{"get":{"tags":["organization-context-controller"],"operationId":"context","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OrganizationContextResponse"}}}}}}},"/api/me":{"get":{"tags":["me-controller"],"operationId":"me","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MeResponse"}}}}}}},"/api/knowledge/search":{"get":{"tags":["knowledge-search-controller"],"summary":"Search permission-verified knowledge evidence","operationId":"searchKnowledge","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/KnowledgeSearchResponse"}}}}}}},"/api/knowledge/catalog":{"get":{"tags":["knowledge-catalog-controller"],"summary":"List current permission-verified Knowledge versions for composition","operationId":"listKnowledgeCatalog","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/KnowledgeCatalogItem"}}}}}}}},"/api/knowledge-spaces/{knowledgeSpaceId}/graph/export":{"get":{"tags":["knowledge-graph-management-controller"],"summary":"Export only graph evidence visible to the current user","operationId":"exportKnowledgeGraph","parameters":[{"name":"knowledgeSpaceId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"format","in":"query","required":false,"schema":{"type":"string","default":"JSON","enum":["JSON","CSV","MARKDOWN","TEXT"]}},{"name":"X-Request-Id","in":"header","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"string"}}}}}}},"/api/knowledge-spaces/{knowledgeSpaceId}/graph/explorer":{"get":{"tags":["knowledge-graph-explorer-controller"],"summary":"Read a bounded permission-filtered graph view","operationId":"exploreKnowledgeGraph","parameters":[{"name":"knowledgeSpaceId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"q","in":"query","required":false,"schema":{"type":"string"}},{"name":"entityLimit","in":"query","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"maxDepth","in":"query","required":false,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/KnowledgeGraphView"}}}}}}},"/api/knowledge-spaces/visible":{"get":{"tags":["knowledge-space-controller"],"summary":"List Knowledge Spaces visible to the current user","operationId":"listVisibleKnowledgeSpaces","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/KnowledgeSpaceResponse"}}}}}}}},"/api/knowledge-spaces/upload-targets":{"get":{"tags":["knowledge-space-controller"],"summary":"List Knowledge Spaces where the current user may add knowledge","operationId":"listKnowledgeSpaceUploadTargets","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/KnowledgeSpaceResponse"}}}}}}}},"/api/knowledge-assets/graph-jobs/{jobId}":{"get":{"tags":["knowledge-asset-lifecycle-controller"],"summary":"Read graph indexing lifecycle status","operationId":"getGraphIndexJob","parameters":[{"name":"jobId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/GraphIndexJobView"}}}}}}},"/api/health":{"get":{"tags":["health-controller"],"operationId":"health","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"object","additionalProperties":{"type":"string"}}}}}}}},"/api/citations/{chunkId}/content":{"get":{"tags":["citation-content-controller"],"summary":"Stream permission-verified source evidence","operationId":"readCitationContent","parameters":[{"name":"chunkId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/StreamingResponseBody"}}}}}}},"/api/assistant/tools/assets/{assetId}/releases/{releaseId}/work-instruction":{"get":{"tags":["assistant-asset-tool-controller"],"summary":"Guide an exact Work Instruction release","operationId":"followAssistantWorkInstruction","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/WorkInstructionToolResult"}}}}}}},"/api/assistant/tools/assets/{assetId}/releases/{releaseId}/prompt-form":{"get":{"tags":["assistant-asset-tool-controller"],"summary":"Resolve the variables required by an exact Prompt release","operationId":"prepareAssistantPrompt","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PromptFormResult"}}}}}}},"/api/assistant/tools/asset-recommendations":{"get":{"tags":["assistant-asset-tool-controller"],"summary":"Recommend exact usable Asset releases without leaking denied candidates","operationId":"recommendAssistantAssets","parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string"}},{"name":"type","in":"query","required":false,"schema":{"type":"string","enum":["PROMPT_TEMPLATE","WORK_INSTRUCTION","CAPABILITY_PACK"]}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/RecommendationResult"}}}}}}},"/api/assistant/conversations":{"get":{"tags":["assistant-controller"],"summary":"List the current actor's conversations by recent activity","operationId":"listAssistantConversations","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AssistantConversationSummary"}}}}}}}},"/api/assistant/conversations/{conversationId}/messages":{"get":{"tags":["assistant-controller"],"summary":"Replay a tenant- and actor-scoped full conversation transcript","operationId":"getAssistantConversationHistory","parameters":[{"name":"conversationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AssistantConversationMessageView"}}}}}}}},"/api/assets/{assetId}":{"get":{"tags":["asset-registry-controller"],"summary":"Read an authorized Asset and its governance history","operationId":"getAsset","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AssetView"}}}}}}},"/api/assets/{assetId}/releases/{releaseId}/work-instruction":{"get":{"tags":["asset-consumption-controller"],"summary":"Follow an exact authorized Work Instruction release","operationId":"followWorkInstruction","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/WorkInstructionView"}}}}}}},"/api/assets/{assetId}/releases/{releaseId}/pack-journey":{"get":{"tags":["asset-consumption-controller"],"summary":"Read an actor-scoped Capability Pack journey","operationId":"getCapabilityPackJourney","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PackJourney"}}}}}}},"/api/asset-delivery":{"get":{"tags":["asset-delivery-controller"],"summary":"Search exact released Assets authorized for the current actor","operationId":"searchReleasedAssets","parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string"}},{"name":"type","in":"query","required":false,"schema":{"type":"string","enum":["PROMPT_TEMPLATE","WORK_INSTRUCTION","CAPABILITY_PACK"]}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AssetRecommendation"}}}}}}}},"/api/asset-delivery/{assetId}":{"get":{"tags":["asset-delivery-controller"],"summary":"Read the latest usable immutable release for an Asset","operationId":"getLatestReleasedAsset","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AssetDeliveryRelease"}}}}}}},"/api/asset-delivery/{assetId}/releases/{releaseId}":{"get":{"tags":["asset-delivery-controller"],"summary":"Read one exact usable immutable Asset release","operationId":"getReleasedAsset","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AssetDeliveryRelease"}}}}}}},"/api/asset-delivery/{assetId}/releases/{releaseId}/relations":{"get":{"tags":["asset-delivery-controller"],"summary":"Resolve only independently authorized relations of an exact release","operationId":"resolveReleasedAssetRelations","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AssetRelationResolution"}}}}}}},"/api/asset-delivery/{assetId}/releases/{releaseId}/pack":{"get":{"tags":["asset-delivery-controller"],"summary":"Read a Pack definition with independently authorized pinned items","operationId":"getReleasedCapabilityPack","parameters":[{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"releaseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CapabilityPackDefinition"}}}}}}},"/api/admin/users":{"get":{"tags":["admin-user-controller"],"summary":"List internal users with their sign-in and mapping status","operationId":"listAdminUsers","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AdminUserResponse"}}}}}}}},"/api/admin/users/{userId}/permissions":{"get":{"tags":["admin-permission-controller"],"summary":"Resolve a user's organization permissions as the engine currently answers them","operationId":"listAdminUserPermissions","parameters":[{"name":"userId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/EffectivePermissionResponse"}}}}}}},"/api/admin/source-principals":{"get":{"tags":["admin-source-access-controller"],"summary":"List observed principals and their mapping","operationId":"listAdminSourcePrincipals","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AdminSourcePrincipalResponse"}}}}}}}},"/api/admin/source-groups":{"get":{"tags":["admin-source-access-controller"],"summary":"List source groups with their sealed membership","operationId":"listAdminSourceGroups","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AdminSourceGroupResponse"}}}}}}}},"/api/admin/source-connections":{"get":{"tags":["admin-source-access-controller"],"summary":"List observed connections and their trust level","operationId":"listAdminSourceConnections","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AdminSourceConnectionResponse"}}}}}}}},"/api/admin/roles":{"get":{"tags":["admin-role-controller"],"summary":"List roles and who is assigned to them","operationId":"listAdminRoles","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AdminRoleListResponse"}}}}}}},"/api/admin/knowledge-spaces/grant-options":{"get":{"tags":["admin-knowledge-space-controller"],"summary":"List the subject shapes each Knowledge Space relation accepts","operationId":"listAdminKnowledgeSpaceGrantOptions","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/KnowledgeSpaceGrantOptionResponse"}}}}}}}},"/api/admin/connectors/{sourceSystem}":{"get":{"tags":["admin-connector-controller"],"summary":"List a source's connections and their crawl settings","operationId":"listAdminConnections","parameters":[{"name":"sourceSystem","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AdminConnectionResponse"}}}}}}}},"/api/admin/connectors/{sourceSystem}/{connectionKey}/scopes":{"get":{"tags":["admin-connector-controller"],"summary":"List what a connection can be pointed at","operationId":"listAdminConnectionScopes","parameters":[{"name":"sourceSystem","in":"path","required":true,"schema":{"type":"string"}},{"name":"connectionKey","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AdminConnectorScopeResponse"}}}}}}}},"/api/admin/connectors/{sourceSystem}/{connectionKey}/activity":{"get":{"tags":["admin-connector-controller"],"summary":"Read what a connection has crawled and what went wrong","operationId":"getAdminConnectionActivity","parameters":[{"name":"sourceSystem","in":"path","required":true,"schema":{"type":"string"}},{"name":"connectionKey","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AdminConnectionActivityResponse"}}}}}}},"/api/admin/connectors/sources":{"get":{"tags":["admin-connector-controller"],"summary":"List the sources this deployment can ingest","operationId":"listAdminConnectorSources","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AdminConnectorSourceResponse"}}}}}}}},"/api/knowledge-spaces/{knowledgeSpaceId}/graph/curations/{curationId}":{"delete":{"tags":["knowledge-graph-management-controller"],"summary":"Reverse a graph curation record","operationId":"deactivateGraphCuration","parameters":[{"name":"knowledgeSpaceId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"curationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"authorizationGeneration","in":"query","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"reason","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}}},"/api/knowledge-assets/{knowledgeAssetId}":{"delete":{"tags":["knowledge-asset-lifecycle-controller"],"summary":"Retire a Knowledge Asset and remove its derived graph","operationId":"deleteKnowledgeAsset","parameters":[{"name":"knowledgeAssetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/KnowledgeAssetRef"}}}}}}},"/api/admin/roles/{role}/members/{userId}":{"delete":{"tags":["admin-role-controller"],"summary":"Remove a user from a role","operationId":"revokeAdminRole","parameters":[{"name":"role","in":"path","required":true,"schema":{"type":"string"}},{"name":"userId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"No Content"}}}},"/api/admin/invitations/{invitationId}":{"delete":{"tags":["admin-invitation-controller"],"summary":"Withdraw an invitation that has not been used","operationId":"revokeAdminInvitation","parameters":[{"name":"invitationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"No Content"}}}}},"components":{"schemas":{"PackProgressRequest":{"type":"object","properties":{"completed":{"type":"boolean"},"confirmed":{"type":"boolean"}}},"Item":{"type":"object","properties":{"key":{"type":"string"},"required":{"type":"boolean"},"order":{"type":"integer","format":"int32"},"kind":{"type":"string"},"resourceId":{"type":"string","format":"uuid"},"pinnedVersionId":{"type":"string","format":"uuid"},"title":{"type":"string"},"versionLabel":{"type":"string"},"availability":{"type":"string","enum":["AVAILABLE","DEPRECATED","WITHDRAWN"]},"completed":{"type":"boolean"},"completedAt":{"type":"string","format":"date-time"}}},"PackJourney":{"type":"object","properties":{"assignmentId":{"type":"string","format":"uuid"},"packAssetId":{"type":"string","format":"uuid"},"packReleaseId":{"type":"string","format":"uuid"},"releaseDigest":{"type":"string"},"title":{"type":"string"},"versionLabel":{"type":"string"},"purpose":{"type":"string","enum":["ROLE_ONBOARDING","HANDOVER","ROLE_ENABLEMENT"]},"audience":{"type":"string"},"expectedOutcome":{"type":"string"},"status":{"type":"string","enum":["IN_PROGRESS","COMPLETED"]},"accessGap":{"type":"boolean"},"completedAccessibleItems":{"type":"integer","format":"int32"},"items":{"type":"array","items":{"$ref":"#/components/schemas/Item"}},"startedAt":{"type":"string","format":"date-time"},"completedAt":{"type":"string","format":"date-time"}}},"PackToolResult":{"type":"object","properties":{"traceId":{"type":"string","format":"uuid"},"journey":{"$ref":"#/components/schemas/PackJourney"}}},"UpdateAssetDraftRequest":{"type":"object","properties":{"expectedLockVersion":{"type":"integer","format":"int64"},"title":{"type":"string"},"summary":{"type":"string"},"classification":{"type":"string"},"schemaVersion":{"type":"string"},"payload":{"type":"string"}}},"AssetView":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["PROMPT_TEMPLATE","WORK_INSTRUCTION","CAPABILITY_PACK"]},"namespace":{"type":"string"},"slug":{"type":"string"},"knowledgeSpaceId":{"type":"string","format":"uuid"},"portfolioState":{"type":"string","enum":["DRAFT_ONLY","ACTIVE","SUNSETTING","RETIRED"]},"authorizationReady":{"type":"boolean"},"draft":{"$ref":"#/components/schemas/Draft"},"revisions":{"type":"array","items":{"$ref":"#/components/schemas/Revision"}},"reviews":{"type":"array","items":{"$ref":"#/components/schemas/Review"}},"releases":{"type":"array","items":{"$ref":"#/components/schemas/Release"}},"ownershipHealth":{"$ref":"#/components/schemas/OwnershipHealth"},"roleAssignments":{"type":"array","items":{"$ref":"#/components/schemas/RoleAssignment"}}}},"AvailabilityEvent":{"type":"object","properties":{"availability":{"type":"string","enum":["AVAILABLE","DEPRECATED","WITHDRAWN"]},"reason":{"type":"string"},"changedByUserId":{"type":"string","format":"uuid"},"effectiveAt":{"type":"string","format":"date-time"}}},"Decision":{"type":"object","properties":{"reviewerUserId":{"type":"string","format":"uuid"},"decision":{"type":"string","enum":["REQUEST_CHANGES","REJECT","APPROVE","CANCEL"]},"comment":{"type":"string"},"decidedAt":{"type":"string","format":"date-time"}}},"Draft":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"lockVersion":{"type":"integer","format":"int64"},"title":{"type":"string"},"summary":{"type":"string"},"classification":{"type":"string"},"schemaVersion":{"type":"string"},"payload":{"type":"string"},"editedByUserId":{"type":"string","format":"uuid"},"updatedAt":{"type":"string","format":"date-time"}}},"OwnershipHealth":{"type":"object","properties":{"ownerPresent":{"type":"boolean"},"backupOwnerPresent":{"type":"boolean"},"orphaned":{"type":"boolean"},"continuityAtRisk":{"type":"boolean"}}},"Release":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"revisionId":{"type":"string","format":"uuid"},"sequence":{"type":"integer","format":"int64"},"versionLabel":{"type":"string"},"title":{"type":"string"},"summary":{"type":"string"},"classification":{"type":"string"},"schemaVersion":{"type":"string"},"payload":{"type":"string"},"digest":{"type":"string"},"releasedByUserId":{"type":"string","format":"uuid"},"releasedAt":{"type":"string","format":"date-time"},"availability":{"type":"string","enum":["AVAILABLE","DEPRECATED","WITHDRAWN"]},"availabilityHistory":{"type":"array","items":{"$ref":"#/components/schemas/AvailabilityEvent"}}}},"Review":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"revisionId":{"type":"string","format":"uuid"},"revisionDigest":{"type":"string"},"state":{"type":"string","enum":["IN_REVIEW","CHANGES_REQUESTED","REJECTED","CANCELLED","APPROVED"]},"policyVersion":{"type":"string"},"requestedByUserId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"resolvedAt":{"type":"string","format":"date-time"},"decisions":{"type":"array","items":{"$ref":"#/components/schemas/Decision"}}}},"Revision":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"sequence":{"type":"integer","format":"int64"},"title":{"type":"string"},"summary":{"type":"string"},"classification":{"type":"string"},"schemaVersion":{"type":"string"},"payload":{"type":"string"},"digest":{"type":"string"},"changeNote":{"type":"string"},"createdByUserId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"}}},"RoleAssignment":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"principalType":{"type":"string"},"principalId":{"type":"string"},"role":{"type":"string","enum":["OWNER","BACKUP_OWNER","STEWARD","VIEWER","EDITOR","REVIEWER","PUBLISHER"]},"validFrom":{"type":"string","format":"date-time"},"validUntil":{"type":"string","format":"date-time"},"assignedByUserId":{"type":"string","format":"uuid"},"projectedAt":{"type":"string","format":"date-time"}}},"ConfirmMappingRequest":{"type":"object","properties":{"appUserId":{"type":"string","format":"uuid"}}},"AdminSourceMappingResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"appUserId":{"type":"string","format":"uuid"},"appUserName":{"type":"string"},"appUserEmail":{"type":"string"},"method":{"type":"string","enum":["IDP_JOIN","SSO_EMAIL_JOIN","SELF_CLAIM","ADMIN_CONFIRMED"]},"status":{"type":"string","enum":["ACTIVE","REVOKED"]},"evidence":{"type":"string"},"verifiedAt":{"type":"string","format":"date-time"}}},"AdminSourcePrincipalResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"sourceSystem":{"type":"string"},"sourceConnectionKey":{"type":"string"},"externalKey":{"type":"string"},"kind":{"type":"string","enum":["SOURCE_USER","SOURCE_GROUP"]},"observedEmail":{"type":"string"},"observedDisplayName":{"type":"string"},"ssoVerified":{"type":"boolean"},"lastSeenAt":{"type":"string","format":"date-time"},"mapping":{"$ref":"#/components/schemas/AdminSourceMappingResponse"}}},"IdentityTrustRequest":{"type":"object","properties":{"sourceSystem":{"type":"string"},"sourceConnectionKey":{"type":"string"},"identityTrust":{"type":"string","enum":["UNTRUSTED","SSO_VERIFIED"]}}},"AdminSourceConnectionResponse":{"type":"object","properties":{"sourceSystem":{"type":"string"},"sourceConnectionKey":{"type":"string"},"identityTrust":{"type":"string","enum":["UNTRUSTED","SSO_VERIFIED"]},"trustDecidedByUserId":{"type":"string","format":"uuid"},"trustDecidedAt":{"type":"string","format":"date-time"},"userCount":{"type":"integer","format":"int32"},"mappedUserCount":{"type":"integer","format":"int32"},"unmappedUserCount":{"type":"integer","format":"int32"},"groupCount":{"type":"integer","format":"int32"},"lastSeenAt":{"type":"string","format":"date-time"}}},"ConfigureConnectionRequest":{"type":"object","properties":{"crawlEnabled":{"type":"boolean"},"knowledgeSpaceId":{"type":"string","format":"uuid"},"actorUserId":{"type":"string","format":"uuid"},"sourceConfig":{"type":"object","additionalProperties":{}},"contentCrawlIntervalSeconds":{"type":"integer","format":"int64"}}},"AdminConnectionResponse":{"type":"object","properties":{"sourceSystem":{"type":"string"},"sourceConnectionKey":{"type":"string"},"identityTrust":{"type":"string","enum":["UNTRUSTED","SSO_VERIFIED"]},"crawlEnabled":{"type":"boolean"},"knowledgeSpaceId":{"type":"string","format":"uuid"},"actorUserId":{"type":"string","format":"uuid"},"sourceConfig":{"type":"object","additionalProperties":{}},"contentCrawlIntervalSeconds":{"type":"integer","format":"int64"},"credentialSet":{"type":"boolean"},"credentialSetByUserId":{"type":"string","format":"uuid"},"credentialSetAt":{"type":"string","format":"date-time"},"configuredByUserId":{"type":"string","format":"uuid"},"configuredAt":{"type":"string","format":"date-time"}}},"ConnectorCredentialRequest":{"type":"object","properties":{"credential":{"type":"string"}}},"SourceResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"sourceSystem":{"type":"string"},"aclAuthority":{"type":"string"},"status":{"type":"string"},"classification":{"type":"string"},"fileName":{"type":"string"},"mediaType":{"type":"string"},"contentLength":{"type":"integer","format":"int64"},"failureCode":{"type":"string"},"failureMessage":{"type":"string"},"embeddingProfileKey":{"type":"string"},"embeddingProvider":{"type":"string"},"embeddingModel":{"type":"string"},"embeddingDimensions":{"type":"integer","format":"int32"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"SuppressIdentityRequest":{"type":"object","properties":{"idempotencyKey":{"type":"string"},"reason":{"type":"string"},"authorizationGeneration":{"type":"integer","format":"int64"},"kind":{"type":"string","enum":["ENTITY","RELATION"]},"identityId":{"type":"string","format":"uuid"}}},"CuratedEntity":{"allOf":[{"$ref":"#/components/schemas/GraphCurationRecord"},{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"namespace":{"$ref":"#/components/schemas/ProjectionNamespace"},"entity":{"$ref":"#/components/schemas/GraphIdentityRef"},"name":{"type":"string"},"type":{"type":"string"},"description":{"type":"string"},"governingEvidence":{"$ref":"#/components/schemas/EvidenceReference"},"provenance":{"$ref":"#/components/schemas/CurationProvenance"}}}]},"CuratedRelation":{"allOf":[{"$ref":"#/components/schemas/GraphCurationRecord"},{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"namespace":{"$ref":"#/components/schemas/ProjectionNamespace"},"relation":{"$ref":"#/components/schemas/GraphIdentityRef"},"sourceEntity":{"$ref":"#/components/schemas/GraphIdentityRef"},"targetEntity":{"$ref":"#/components/schemas/GraphIdentityRef"},"type":{"type":"string"},"keywords":{"type":"array","items":{"type":"string"}},"description":{"type":"string"},"weight":{"type":"number","format":"double"},"governingEvidence":{"$ref":"#/components/schemas/EvidenceReference"},"provenance":{"$ref":"#/components/schemas/CurationProvenance"}}}]},"CurationProvenance":{"type":"object","properties":{"actorUserId":{"type":"string","format":"uuid"},"authorizationModelId":{"type":"string"},"aclGeneration":{"type":"integer","format":"int64"},"curatedAt":{"type":"string","format":"date-time"},"reason":{"type":"string"}}},"EvidenceReference":{"type":"object","properties":{"organizationId":{"type":"string","format":"uuid"},"knowledgeAssetId":{"type":"string","format":"uuid"},"sourceRevisionId":{"type":"string","format":"uuid"},"chunkId":{"type":"string","format":"uuid"},"aclSnapshotId":{"type":"string","format":"uuid"},"aclGeneration":{"type":"integer","format":"int64"},"chunk":{"type":"boolean"}}},"GraphCurationRecord":{},"GraphIdentityRef":{"type":"object","properties":{"kind":{"type":"string","enum":["ENTITY","RELATION"]},"id":{"type":"string","format":"uuid"}}},"IdentityAlias":{"allOf":[{"$ref":"#/components/schemas/GraphCurationRecord"},{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"namespace":{"$ref":"#/components/schemas/ProjectionNamespace"},"source":{"$ref":"#/components/schemas/GraphIdentityRef"},"target":{"$ref":"#/components/schemas/GraphIdentityRef"},"provenance":{"$ref":"#/components/schemas/CurationProvenance"}}}]},"IdentitySuppression":{"allOf":[{"$ref":"#/components/schemas/GraphCurationRecord"},{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"namespace":{"$ref":"#/components/schemas/ProjectionNamespace"},"identity":{"$ref":"#/components/schemas/GraphIdentityRef"},"provenance":{"$ref":"#/components/schemas/CurationProvenance"}}}]},"ProjectionNamespace":{"type":"object","properties":{"organizationId":{"type":"string","format":"uuid"},"workspace":{"type":"string"},"collection":{"type":"string"}}},"CurateRelationRequest":{"type":"object","properties":{"idempotencyKey":{"type":"string"},"reason":{"type":"string"},"authorizationGeneration":{"type":"integer","format":"int64"},"relationId":{"type":"string","format":"uuid"},"sourceEntityId":{"type":"string","format":"uuid"},"targetEntityId":{"type":"string","format":"uuid"},"type":{"type":"string"},"keywords":{"type":"array","items":{"type":"string"}},"description":{"type":"string"},"weight":{"type":"number","format":"double"},"evidence":{"$ref":"#/components/schemas/EvidenceRequest"}}},"EvidenceRequest":{"type":"object","properties":{"organizationId":{"type":"string","format":"uuid"},"knowledgeAssetId":{"type":"string","format":"uuid"},"sourceRevisionId":{"type":"string","format":"uuid"},"chunkId":{"type":"string","format":"uuid"},"aclSnapshotId":{"type":"string","format":"uuid"},"aclGeneration":{"type":"integer","format":"int64"}}},"CurateEntityRequest":{"type":"object","properties":{"idempotencyKey":{"type":"string"},"reason":{"type":"string"},"authorizationGeneration":{"type":"integer","format":"int64"},"entityId":{"type":"string","format":"uuid"},"name":{"type":"string"},"type":{"type":"string"},"description":{"type":"string"},"evidence":{"$ref":"#/components/schemas/EvidenceRequest"}}},"AliasIdentityRequest":{"type":"object","properties":{"idempotencyKey":{"type":"string"},"reason":{"type":"string"},"authorizationGeneration":{"type":"integer","format":"int64"},"kind":{"type":"string","enum":["ENTITY","RELATION"]},"sourceIdentityId":{"type":"string","format":"uuid"},"targetIdentityId":{"type":"string","format":"uuid"}}},"GraphIndexJobView":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"knowledgeAssetId":{"type":"string","format":"uuid"},"knowledgeAssetVersionId":{"type":"string","format":"uuid"},"sourceRevisionId":{"type":"string","format":"uuid"},"projectionGeneration":{"type":"integer","format":"int64"},"graphProcessingProfileId":{"type":"string","format":"uuid"},"graphProcessingProfileSha256":{"type":"string"},"status":{"type":"string"},"attempt":{"type":"integer","format":"int32"},"cancellationRequested":{"type":"boolean"},"cancellationRequestedAt":{"type":"string","format":"date-time"},"lastErrorCode":{"type":"string"},"lastErrorMessage":{"type":"string"},"completedAt":{"type":"string","format":"date-time"}}},"KnowledgeSearchRequest":{"type":"object","properties":{"query":{"type":"string"},"requestId":{"type":"string"}}},"KnowledgeCitation":{"type":"object","properties":{"chunkId":{"type":"string","format":"uuid"},"knowledgeAssetId":{"type":"string","format":"uuid"},"sourceRevisionId":{"type":"string","format":"uuid"},"title":{"type":"string"},"startPage":{"type":"integer","format":"int32"},"endPage":{"type":"integer","format":"int32"},"heading":{"type":"string"}}},"KnowledgeResult":{"type":"object","properties":{"traceId":{"type":"string","format":"uuid"},"requestId":{"type":"string"},"citations":{"type":"array","items":{"$ref":"#/components/schemas/KnowledgeCitation"}}}},"PromptRunRequest":{"type":"object","properties":{"variables":{"type":"object","additionalProperties":{}},"knowledgeQuery":{"type":"string"},"requestId":{"type":"string"},"confirmedExternalProvider":{"type":"boolean"}}},"AiRoute":{"type":"object","properties":{"gatewayId":{"type":"string"},"modelId":{"type":"string"}}},"PromptCitation":{"type":"object","properties":{"chunkId":{"type":"string","format":"uuid"},"knowledgeAssetId":{"type":"string","format":"uuid"},"sourceRevisionId":{"type":"string","format":"uuid"},"title":{"type":"string"},"startPage":{"type":"integer","format":"int32"},"endPage":{"type":"integer","format":"int32"},"heading":{"type":"string"}}},"PromptRunResult":{"type":"object","properties":{"runId":{"type":"string","format":"uuid"},"assetId":{"type":"string","format":"uuid"},"releaseId":{"type":"string","format":"uuid"},"releaseDigest":{"type":"string"},"modelRoute":{"$ref":"#/components/schemas/AiRoute"},"output":{"type":"string"},"citations":{"type":"array","items":{"$ref":"#/components/schemas/PromptCitation"}},"durationMillis":{"type":"integer","format":"int64"}}},"PromptRunToolResult":{"type":"object","properties":{"traceId":{"type":"string","format":"uuid"},"result":{"$ref":"#/components/schemas/PromptRunResult"}}},"PromptRenderRequest":{"type":"object","properties":{"variables":{"type":"object","additionalProperties":{}}}},"PromptRenderResult":{"type":"object","properties":{"assetId":{"type":"string","format":"uuid"},"releaseId":{"type":"string","format":"uuid"},"releaseDigest":{"type":"string"},"systemInstruction":{"type":"string"},"userPrompt":{"type":"string"},"sensitiveVariables":{"type":"array","items":{"type":"string"}},"inputShapeDigest":{"type":"string"}}},"PromptRenderToolResult":{"type":"object","properties":{"traceId":{"type":"string","format":"uuid"},"result":{"$ref":"#/components/schemas/PromptRenderResult"}}},"ConfirmedActionRequest":{"type":"object","properties":{"confirmed":{"type":"boolean"}}},"ForkRequest":{"type":"object","properties":{"namespace":{"type":"string"},"slug":{"type":"string"},"knowledgeSpaceId":{"type":"string","format":"uuid"},"confirmed":{"type":"boolean"}}},"ForkResult":{"type":"object","properties":{"traceId":{"type":"string","format":"uuid"},"asset":{"$ref":"#/components/schemas/AssetView"}}},"FeedbackRequest":{"type":"object","properties":{"type":{"type":"string","enum":["HELPFUL","OUTDATED","INCORRECT","OTHER"]},"comment":{"type":"string"},"confirmed":{"type":"boolean"}}},"FeedbackResult":{"type":"object","properties":{"traceId":{"type":"string","format":"uuid"},"feedbackId":{"type":"string","format":"uuid"}}},"AssistantChatRequest":{"type":"object","properties":{"message":{"type":"string","maxLength":4000,"minLength":0},"limit":{"type":"integer","format":"int32"},"conversationId":{"type":"string","format":"uuid"}},"required":["message"]},"ServerSentEventString":{},"AssetDraftRequest":{"type":"object","properties":{"title":{"type":"string"},"summary":{"type":"string"},"classification":{"type":"string"},"schemaVersion":{"type":"string"},"payload":{"type":"string"}}},"CreateAssetRequest":{"type":"object","properties":{"type":{"type":"string","enum":["PROMPT_TEMPLATE","WORK_INSTRUCTION","CAPABILITY_PACK"]},"namespace":{"type":"string"},"slug":{"type":"string"},"knowledgeSpaceId":{"type":"string","format":"uuid"},"draft":{"$ref":"#/components/schemas/AssetDraftRequest"}}},"SubmitAssetRevisionRequest":{"type":"object","properties":{"changeNote":{"type":"string"}}},"AssignAssetRoleRequest":{"type":"object","properties":{"principalType":{"type":"string"},"principalId":{"type":"string"},"role":{"type":"string","enum":["OWNER","BACKUP_OWNER","STEWARD","VIEWER","EDITOR","REVIEWER","PUBLISHER"]}}},"AssetReviewDecisionRequest":{"type":"object","properties":{"decision":{"type":"string","enum":["REQUEST_CHANGES","REJECT","APPROVE","CANCEL"]},"comment":{"type":"string"}}},"PublishAssetReleaseRequest":{"type":"object","properties":{"revisionId":{"type":"string","format":"uuid"},"versionLabel":{"type":"string"}}},"Step":{"type":"object","properties":{"key":{"type":"string"},"title":{"type":"string"},"instruction":{"type":"string"},"expectedResult":{"type":"string"},"check":{"type":"string"},"escalation":{"type":"string"},"prohibitedActions":{"type":"array","items":{"type":"string"}},"relatedAssetIds":{"type":"array","items":{"type":"string","format":"uuid"}},"relatedKnowledgeVersionIds":{"type":"array","items":{"type":"string","format":"uuid"}}}},"WorkInstructionSpec":{"type":"object","properties":{"purpose":{"type":"string"},"audience":{"type":"string"},"prerequisites":{"type":"array","items":{"type":"string"}},"completionOutcome":{"type":"string"},"responsibleRole":{"type":"string"},"steps":{"type":"array","items":{"$ref":"#/components/schemas/Step"}}}},"WorkInstructionView":{"type":"object","properties":{"assetId":{"type":"string","format":"uuid"},"releaseId":{"type":"string","format":"uuid"},"releaseDigest":{"type":"string"},"title":{"type":"string"},"versionLabel":{"type":"string"},"instruction":{"$ref":"#/components/schemas/WorkInstructionSpec"},"acknowledged":{"type":"boolean"},"acknowledgedAt":{"type":"string","format":"date-time"}}},"AssetAvailabilityRequest":{"type":"object","properties":{"reason":{"type":"string"}}},"PromptVariablesRequest":{"type":"object","properties":{"variables":{"type":"object","additionalProperties":{}}}},"CaseResult":{"type":"object","properties":{"name":{"type":"string"},"passed":{"type":"boolean"},"failedAssertions":{"type":"array","items":{"type":"string"}},"promptRunId":{"type":"string","format":"uuid"}}},"PromptEvaluationResult":{"type":"object","properties":{"evaluationId":{"type":"string","format":"uuid"},"assetId":{"type":"string","format":"uuid"},"releaseId":{"type":"string","format":"uuid"},"releaseDigest":{"type":"string"},"passedCases":{"type":"integer","format":"int32"},"totalCases":{"type":"integer","format":"int32"},"cases":{"type":"array","items":{"$ref":"#/components/schemas/CaseResult"}}}},"ForkReleaseRequest":{"type":"object","properties":{"namespace":{"type":"string"},"slug":{"type":"string"},"knowledgeSpaceId":{"type":"string","format":"uuid"}}},"PromptComparisonRequest":{"type":"object","properties":{"baselineReleaseId":{"type":"string","format":"uuid"},"candidateReleaseId":{"type":"string","format":"uuid"}}},"PromptEvaluationComparison":{"type":"object","properties":{"baseline":{"$ref":"#/components/schemas/PromptEvaluationResult"},"candidate":{"$ref":"#/components/schemas/PromptEvaluationResult"},"passedCaseDelta":{"type":"integer","format":"int32"}}},"AssignRoleRequest":{"type":"object","properties":{"userId":{"type":"string","format":"uuid"}}},"CreateKnowledgeSpaceRequest":{"type":"object","properties":{"name":{"type":"string"},"departmentId":{"type":"string","format":"uuid"}}},"AdminKnowledgeSpaceResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"key":{"type":"string"},"name":{"type":"string"},"departmentId":{"type":"string","format":"uuid"},"active":{"type":"boolean"},"grants":{"type":"array","items":{"$ref":"#/components/schemas/KnowledgeSpaceGrantResponse"}},"grantsComplete":{"type":"boolean"},"policyVersion":{"type":"string"}}},"KnowledgeSpaceGrantResponse":{"type":"object","properties":{"relation":{"type":"string"},"subject":{"type":"string"}}},"GrantKnowledgeSpaceAccessRequest":{"type":"object","properties":{"relation":{"type":"string"},"kind":{"type":"string","enum":["ORGANIZATION","DEPARTMENT","DEPARTMENT_MANAGERS","ROLE","USER"]},"subjectId":{"type":"string","format":"uuid"},"role":{"type":"string"}}},"CreateInvitationRequest":{"type":"object","properties":{"email":{"type":"string"},"role":{"type":"string","enum":["EMPLOYEE","TEAM_LEAD","MANAGER","DIRECTOR","EXECUTIVE","ADMIN"]},"departmentId":{"type":"string","format":"uuid"}}},"AdminInvitationResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"email":{"type":"string"},"role":{"type":"string","enum":["EMPLOYEE","TEAM_LEAD","MANAGER","DIRECTOR","EXECUTIVE","ADMIN"]},"departmentId":{"type":"string","format":"uuid"},"status":{"type":"string"},"invitedAt":{"type":"string","format":"date-time"},"acceptedAt":{"type":"string","format":"date-time"},"acceptedAppUserId":{"type":"string","format":"uuid"}}},"AdminConnectorProbeResponse":{"type":"object","properties":{"authenticated":{"type":"boolean"},"connectionKey":{"type":"string"},"accountName":{"type":"string"},"identityName":{"type":"string"},"canReadContent":{"type":"boolean"},"errorCode":{"type":"string"}}},"ExplainAccessRequest":{"type":"object","properties":{"userId":{"type":"string","format":"uuid"},"permission":{"type":"string"},"resourceType":{"type":"string"},"resourceId":{"type":"string","format":"uuid"}}},"AccessBlockResponse":{"type":"object","properties":{"branch":{"type":"string"},"kind":{"type":"string"},"detail":{"type":"string"}}},"AccessStepResponse":{"type":"object","properties":{"object":{"type":"string"},"relation":{"type":"string"},"kind":{"type":"string"}}},"AclProvenanceResponse":{"type":"object","properties":{"authority":{"type":"string"},"origin":{"type":"string"},"generation":{"type":"integer","format":"int64"},"capturedAt":{"type":"string","format":"date-time"},"expired":{"type":"boolean"}}},"ExplainAccessResponse":{"type":"object","properties":{"state":{"type":"string","enum":["ALLOWED","DENIED","UNKNOWN"]},"reasonCode":{"type":"string"},"path":{"type":"array","items":{"$ref":"#/components/schemas/AccessStepResponse"}},"blockedBy":{"type":"array","items":{"$ref":"#/components/schemas/AccessBlockResponse"}},"provenance":{"$ref":"#/components/schemas/AclProvenanceResponse"},"policyVersion":{"type":"string"},"evaluatedAt":{"type":"string","format":"date-time"}}},"RenameConversationRequest":{"type":"object","properties":{"title":{"type":"string","maxLength":120,"minLength":0}},"required":["title"]},"UpdateAdminUserRequest":{"type":"object","properties":{"role":{"type":"string","enum":["EMPLOYEE","TEAM_LEAD","MANAGER","DIRECTOR","EXECUTIVE","ADMIN"]},"active":{"type":"boolean"}}},"AdminUserResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"email":{"type":"string"},"role":{"type":"string","enum":["EMPLOYEE","TEAM_LEAD","MANAGER","DIRECTOR","EXECUTIVE","ADMIN"]},"departmentId":{"type":"string","format":"uuid"},"active":{"type":"boolean"},"signInLinked":{"type":"boolean"},"mappedPrincipalCount":{"type":"integer","format":"int32"}}},"SessionResponse":{"type":"object","properties":{"authenticated":{"type":"boolean"},"name":{"type":"string"},"email":{"type":"string"},"userId":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"departmentId":{"type":"string","format":"uuid"},"role":{"type":"string","enum":["EMPLOYEE","TEAM_LEAD","MANAGER","DIRECTOR","EXECUTIVE","ADMIN"]}}},"CsrfResponse":{"type":"object","properties":{"headerName":{"type":"string"},"parameterName":{"type":"string"},"token":{"type":"string"}}},"DepartmentResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"name":{"type":"string"}}},"OrganizationContextResponse":{"type":"object","properties":{"organizationId":{"type":"string","format":"uuid"},"departments":{"type":"array","items":{"$ref":"#/components/schemas/DepartmentResponse"}},"users":{"type":"array","items":{"$ref":"#/components/schemas/UserResponse"}}}},"UserResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"departmentId":{"type":"string","format":"uuid"},"name":{"type":"string"},"email":{"type":"string"},"role":{"type":"string","enum":["EMPLOYEE","TEAM_LEAD","MANAGER","DIRECTOR","EXECUTIVE","ADMIN"]}}},"MeResponse":{"type":"object","properties":{"authenticated":{"type":"boolean"},"subject":{"type":"string"},"email":{"type":"string"},"name":{"type":"string"},"authorizationProvider":{"type":"string"},"userId":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"departmentId":{"type":"string","format":"uuid"},"role":{"type":"string","enum":["EMPLOYEE","TEAM_LEAD","MANAGER","DIRECTOR","EXECUTIVE","ADMIN"]}}},"KnowledgeEvidenceResponse":{"type":"object","properties":{"citationId":{"type":"string","format":"uuid"},"knowledgeAssetId":{"type":"string","format":"uuid"},"title":{"type":"string"},"content":{"type":"string"},"sourceUri":{"type":"string"},"startPage":{"type":"integer","format":"int32"},"endPage":{"type":"integer","format":"int32"},"heading":{"type":"string"},"relevanceScore":{"type":"number","format":"double"}}},"KnowledgeSearchResponse":{"type":"object","properties":{"requestId":{"type":"string"},"evidence":{"type":"array","items":{"$ref":"#/components/schemas/KnowledgeEvidenceResponse"}}}},"KnowledgeCatalogItem":{"type":"object","properties":{"knowledgeAssetId":{"type":"string","format":"uuid"},"knowledgeVersionId":{"type":"string","format":"uuid"},"versionNumber":{"type":"integer","format":"int64"},"knowledgeSpaceId":{"type":"string","format":"uuid"},"title":{"type":"string"},"language":{"type":"string"},"classification":{"type":"string","enum":["PUBLIC","INTERNAL","CONFIDENTIAL","RESTRICTED"]},"contentDigest":{"type":"string"}}},"Entity":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"type":{"type":"string"},"description":{"type":"string"},"citationChunkIds":{"type":"array","items":{"type":"string","format":"uuid"}},"governingEvidence":{"$ref":"#/components/schemas/EvidenceReference"}}},"KnowledgeGraphView":{"type":"object","properties":{"knowledgeSpaceId":{"type":"string","format":"uuid"},"authorizationGeneration":{"type":"integer","format":"int64"},"canCurate":{"type":"boolean"},"entities":{"type":"array","items":{"$ref":"#/components/schemas/Entity"}},"relations":{"type":"array","items":{"$ref":"#/components/schemas/Relation"}},"truncated":{"type":"boolean"}}},"Relation":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"sourceEntityId":{"type":"string","format":"uuid"},"targetEntityId":{"type":"string","format":"uuid"},"type":{"type":"string"},"description":{"type":"string"},"weight":{"type":"number","format":"double"},"keywords":{"type":"array","items":{"type":"string"}},"citationChunkIds":{"type":"array","items":{"type":"string","format":"uuid"}},"governingEvidence":{"$ref":"#/components/schemas/EvidenceReference"}}},"KnowledgeSpaceResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"key":{"type":"string"},"name":{"type":"string"},"departmentId":{"type":"string","format":"uuid"}}},"StreamingResponseBody":{},"WorkInstructionToolResult":{"type":"object","properties":{"traceId":{"type":"string","format":"uuid"},"instruction":{"$ref":"#/components/schemas/WorkInstructionView"}}},"AssistantReleaseRef":{"type":"object","properties":{"assetId":{"type":"string","format":"uuid"},"releaseId":{"type":"string","format":"uuid"},"releaseDigest":{"type":"string"}}},"PromptFormResult":{"type":"object","properties":{"traceId":{"type":"string","format":"uuid"},"release":{"$ref":"#/components/schemas/AssistantReleaseRef"},"objective":{"type":"string"},"audience":{"type":"string"},"variables":{"type":"array","items":{"$ref":"#/components/schemas/Variable"}},"outputContract":{"type":"object","additionalProperties":{}},"knowledgeRequirements":{"type":"array","items":{"type":"string"}},"knownLimitations":{"type":"string"}}},"Variable":{"type":"object","properties":{"name":{"type":"string"},"type":{"type":"string","enum":["STRING","INTEGER","NUMBER","BOOLEAN","STRING_LIST"]},"required":{"type":"boolean"},"defaultValue":{},"sensitive":{"type":"boolean"},"pattern":{"type":"string"},"allowedValues":{"type":"array","items":{"type":"string"}}}},"AssetRecommendation":{"type":"object","properties":{"assetId":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["PROMPT_TEMPLATE","WORK_INSTRUCTION","CAPABILITY_PACK"]},"namespace":{"type":"string"},"slug":{"type":"string"},"title":{"type":"string"},"summary":{"type":"string"},"knowledgeSpaceId":{"type":"string","format":"uuid"},"portfolioState":{"type":"string","enum":["DRAFT_ONLY","ACTIVE","SUNSETTING","RETIRED"]},"releaseId":{"type":"string","format":"uuid"},"versionLabel":{"type":"string"},"releaseDigest":{"type":"string"},"availability":{"type":"string","enum":["AVAILABLE","DEPRECATED","WITHDRAWN"]}}},"RecommendationResult":{"type":"object","properties":{"traceId":{"type":"string","format":"uuid"},"recommendations":{"type":"array","items":{"$ref":"#/components/schemas/AssetRecommendation"}}}},"AssistantConversationSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"lastActivityAt":{"type":"string","format":"date-time"},"messageCount":{"type":"integer","format":"int64"}}},"AssistantConversationMessageView":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"role":{"type":"string","enum":["USER","ASSISTANT"]},"content":{"type":"string"},"sequence":{"type":"integer","format":"int64"},"occurredAt":{"type":"string","format":"date-time"}}},"AssetSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["PROMPT_TEMPLATE","WORK_INSTRUCTION","CAPABILITY_PACK"]},"namespace":{"type":"string"},"slug":{"type":"string"},"title":{"type":"string"},"summary":{"type":"string"},"knowledgeSpaceId":{"type":"string","format":"uuid"},"portfolioState":{"type":"string","enum":["DRAFT_ONLY","ACTIVE","SUNSETTING","RETIRED"]}}},"AssetDeliveryRelease":{"type":"object","properties":{"assetId":{"type":"string","format":"uuid"},"releaseId":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["PROMPT_TEMPLATE","WORK_INSTRUCTION","CAPABILITY_PACK"]},"namespace":{"type":"string"},"slug":{"type":"string"},"versionLabel":{"type":"string"},"title":{"type":"string"},"summary":{"type":"string"},"classification":{"type":"string"},"schemaVersion":{"type":"string"},"payload":{"type":"string"},"digest":{"type":"string"},"availability":{"type":"string","enum":["AVAILABLE","DEPRECATED","WITHDRAWN"]},"releasedAt":{"type":"string","format":"date-time"}}},"AssetRelationResolution":{"type":"object","properties":{"assetId":{"type":"string","format":"uuid"},"releaseId":{"type":"string","format":"uuid"},"accessGap":{"type":"boolean"},"relations":{"type":"array","items":{"$ref":"#/components/schemas/Relation"}}}},"CapabilityPackDefinition":{"type":"object","properties":{"packAssetId":{"type":"string","format":"uuid"},"packReleaseId":{"type":"string","format":"uuid"},"releaseDigest":{"type":"string"},"title":{"type":"string"},"versionLabel":{"type":"string"},"purpose":{"type":"string","enum":["ROLE_ONBOARDING","HANDOVER","ROLE_ENABLEMENT"]},"audience":{"type":"string"},"prerequisites":{"type":"array","items":{"type":"string"}},"expectedOutcome":{"type":"string"},"completionCriteria":{"type":"array","items":{"type":"string"}},"reviewDate":{"type":"string"},"owner":{"type":"string"},"accessGap":{"type":"boolean"},"items":{"type":"array","items":{"$ref":"#/components/schemas/Item"}}}},"EffectivePermissionResponse":{"type":"object","properties":{"userId":{"type":"string","format":"uuid"},"permissions":{"type":"object","additionalProperties":{"type":"string","enum":["ALLOWED","DENIED","UNKNOWN"]}},"evaluatedAt":{"type":"string","format":"date-time"}}},"AdminSourceGroupMemberResponse":{"type":"object","properties":{"principalId":{"type":"string","format":"uuid"},"externalKey":{"type":"string"},"observedDisplayName":{"type":"string"},"observedEmail":{"type":"string"},"appUserId":{"type":"string","format":"uuid"},"appUserName":{"type":"string"}}},"AdminSourceGroupResponse":{"type":"object","properties":{"principalId":{"type":"string","format":"uuid"},"sourceSystem":{"type":"string"},"sourceConnectionKey":{"type":"string"},"externalKey":{"type":"string"},"observedDisplayName":{"type":"string"},"sourceAclSnapshotId":{"type":"string","format":"uuid"},"aclGeneration":{"type":"integer","format":"int64"},"sealedAt":{"type":"string","format":"date-time"},"members":{"type":"array","items":{"$ref":"#/components/schemas/AdminSourceGroupMemberResponse"}}}},"AdminRoleListResponse":{"type":"object","properties":{"roles":{"type":"array","items":{"$ref":"#/components/schemas/AdminRoleResponse"}},"complete":{"type":"boolean"},"policyVersion":{"type":"string"}}},"AdminRoleResponse":{"type":"object","properties":{"role":{"type":"string"},"assignees":{"type":"array","items":{"type":"string"}}}},"KnowledgeSpaceGrantOptionResponse":{"type":"object","properties":{"relation":{"type":"string"},"kinds":{"type":"array","items":{"type":"string","enum":["ORGANIZATION","DEPARTMENT","DEPARTMENT_MANAGERS","ROLE","USER"]}}}},"AdminConnectorScopeResponse":{"type":"object","properties":{"key":{"type":"string"},"displayName":{"type":"string"},"reachable":{"type":"boolean"},"admissible":{"type":"boolean"},"instruction":{"type":"string"}}},"AdminConnectionActivityResponse":{"type":"object","properties":{"sourceSystem":{"type":"string"},"sourceConnectionKey":{"type":"string"},"objectsTotal":{"type":"integer","format":"int64"},"objectsActive":{"type":"integer","format":"int64"},"objectsArchived":{"type":"integer","format":"int64"},"lastObjectAt":{"type":"string","format":"date-time"},"lastCrawlAt":{"type":"string","format":"date-time"},"recentAttempts":{"type":"array","items":{"$ref":"#/components/schemas/AdminCrawlAttemptResponse"}}}},"AdminCrawlAttemptResponse":{"type":"object","properties":{"outcome":{"type":"string"},"objectsMaterialized":{"type":"integer","format":"int32"},"objectsRotated":{"type":"integer","format":"int32"},"objectsRematerialized":{"type":"integer","format":"int32"},"objectsRetired":{"type":"integer","format":"int32"},"objectsFailed":{"type":"integer","format":"int32"},"errorCode":{"type":"string"},"errorMessage":{"type":"string"},"attemptedAt":{"type":"string","format":"date-time"}}},"AdminConnectorSourceResponse":{"type":"object","properties":{"sourceSystem":{"type":"string"},"displayName":{"type":"string"}}},"KnowledgeAssetRef":{"type":"object","properties":{"knowledgeAssetId":{"type":"string","format":"uuid"},"knowledgeAssetVersionId":{"type":"string","format":"uuid"},"normalizedRecordId":{"type":"string","format":"uuid"},"rawSourceObjectId":{"type":"string","format":"uuid"},"sourceAclSnapshotId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["PENDING","ACTIVE","RETIRED"]}}}}}} \ No newline at end of file diff --git a/core/src/main/java/com/orgmemory/core/assetregistry/AssetRegistryCoordinator.java b/core/src/main/java/com/orgmemory/core/assetregistry/AssetRegistryCoordinator.java index 11c07d51..d8702419 100644 --- a/core/src/main/java/com/orgmemory/core/assetregistry/AssetRegistryCoordinator.java +++ b/core/src/main/java/com/orgmemory/core/assetregistry/AssetRegistryCoordinator.java @@ -599,6 +599,7 @@ private AssetView view(Asset asset) { asset.getId(), asset.getOrganizationId()); List assignments = roles.findByAssetIdOrderByValidFromAsc(asset.getId()); + Instant viewedAt = Instant.now(); return new AssetView( asset.getId(), asset.getType(), @@ -620,9 +621,33 @@ private AssetView view(Asset asset) { assetRevisions.stream().map(AssetRegistryCoordinator::revisionView).toList(), assetReviews.stream().map(this::reviewView).toList(), assetReleases.stream().map(this::releaseView).toList(), + ownershipHealth(assignments, viewedAt), assignments.stream().map(AssetRegistryCoordinator::roleView).toList()); } + private static AssetView.OwnershipHealth ownershipHealth( + List assignments, Instant viewedAt) { + boolean ownerPresent = hasActiveRole( + assignments, AssetRole.OWNER, viewedAt); + boolean backupOwnerPresent = hasActiveRole( + assignments, AssetRole.BACKUP_OWNER, viewedAt); + return new AssetView.OwnershipHealth( + ownerPresent, + backupOwnerPresent, + !ownerPresent && !backupOwnerPresent, + !ownerPresent || !backupOwnerPresent); + } + + private static boolean hasActiveRole( + List assignments, + AssetRole role, + Instant viewedAt) { + return assignments.stream().anyMatch(assignment -> + assignment.getRole() == role + && (assignment.getValidUntil() == null + || assignment.getValidUntil().isAfter(viewedAt))); + } + private AssetView.Review reviewView(AssetReviewCase review) { return new AssetView.Review( review.getId(), diff --git a/core/src/main/java/com/orgmemory/core/assetregistry/AssetView.java b/core/src/main/java/com/orgmemory/core/assetregistry/AssetView.java index 574baa97..e5ae99d9 100644 --- a/core/src/main/java/com/orgmemory/core/assetregistry/AssetView.java +++ b/core/src/main/java/com/orgmemory/core/assetregistry/AssetView.java @@ -16,6 +16,7 @@ public record AssetView( List revisions, List reviews, List releases, + OwnershipHealth ownershipHealth, List roleAssignments) { public AssetView { @@ -112,4 +113,11 @@ public record RoleAssignment( UUID assignedByUserId, Instant projectedAt) { } + + public record OwnershipHealth( + boolean ownerPresent, + boolean backupOwnerPresent, + boolean orphaned, + boolean continuityAtRisk) { + } } diff --git a/demo/fixtures/asset-registry/README.md b/demo/fixtures/asset-registry/README.md new file mode 100644 index 00000000..79fa782f --- /dev/null +++ b/demo/fixtures/asset-registry/README.md @@ -0,0 +1,28 @@ +# L1 Support Asset Registry Golden POC + +This synthetic fixture proves the browser-native, governed reuse path without +customer or employee data. + +Stable coordinates: + +- Knowledge: `support.sla-and-escalation@1` +- Work Instruction: `support.classify-and-respond@1.0.0` +- Prompt Template: `support.triage-customer-ticket@1.0.0` +- Capability Pack: `support.l1-onboarding@1.0.0` +- Evaluation rubric: `support.triage-quality@1` + +Files: + +- `support-sla-and-escalation.md` is the permission-aware grounding source. +- `prompt-template.json` is the released Prompt payload with eight bounded + evaluation cases. +- `work-instruction.json` is the released task procedure. +- `capability-pack-template.json` is resolved with exact release UUIDs by the + golden integration test. +- `quality-checklist.json` is the human verification checklist. +- `mock-tickets.json` fixes expected classification, SLA, escalation, and + citation behavior. +- `success-metrics.json` defines the POC metric formulas and thresholds. + +The fixture intentionally contains no executable Skill, Tool, Agent, public +marketplace metadata, Screenpipe event, or MCP mutation. diff --git a/demo/fixtures/asset-registry/capability-pack-template.json b/demo/fixtures/asset-registry/capability-pack-template.json new file mode 100644 index 00000000..ac35b4e6 --- /dev/null +++ b/demo/fixtures/asset-registry/capability-pack-template.json @@ -0,0 +1,36 @@ +{ + "purpose": "ROLE_ONBOARDING", + "audience": "L1 support agent", + "prerequisites": [ + "Active support account", + "Access to the Support Knowledge Space" + ], + "expectedOutcome": "The agent can classify, ground, draft, verify, and record a correct first ticket", + "items": [ + { + "key": "instruction", + "required": true, + "kind": "REGISTRY_RELEASE", + "assetId": "${WORK_INSTRUCTION_ASSET_ID}", + "releaseId": "${WORK_INSTRUCTION_RELEASE_ID}", + "knowledgeAssetId": null, + "knowledgeVersionId": null + }, + { + "key": "prompt", + "required": true, + "kind": "REGISTRY_RELEASE", + "assetId": "${PROMPT_ASSET_ID}", + "releaseId": "${PROMPT_RELEASE_ID}", + "knowledgeAssetId": null, + "knowledgeVersionId": null + } + ], + "completionCriteria": [ + "Both required released items are complete", + "One mock ticket passes support.triage-quality@1", + "The execution trace pins the Prompt release and grounding citation" + ], + "reviewDate": "2026-12-31", + "owner": "Support operations" +} diff --git a/demo/fixtures/asset-registry/mock-tickets.json b/demo/fixtures/asset-registry/mock-tickets.json new file mode 100644 index 00000000..35f6c954 --- /dev/null +++ b/demo/fixtures/asset-registry/mock-tickets.json @@ -0,0 +1,10 @@ +[ + {"id":"TICKET-001","scenario":"billing question","text":"Why is my monthly invoice higher than last month?","category":"billing","slaTier":"P2","escalate":false,"allowedCitations":["support.sla-and-escalation@1"],"rubricPass":true}, + {"id":"TICKET-002","scenario":"password reset","text":"I cannot sign in and need a password reset.","category":"access","slaTier":"P2","escalate":false,"allowedCitations":["support.sla-and-escalation@1"],"rubricPass":true}, + {"id":"TICKET-003","scenario":"degraded service","text":"Reports take ten minutes to load for every user.","category":"degraded_service","slaTier":"P1","escalate":true,"allowedCitations":["support.sla-and-escalation@1"],"rubricPass":true}, + {"id":"TICKET-004","scenario":"confirmed outage","text":"The production API is unavailable in all regions.","category":"outage","slaTier":"P0","escalate":true,"allowedCitations":["support.sla-and-escalation@1"],"rubricPass":true}, + {"id":"TICKET-005","scenario":"suspected security issue","text":"I saw an unknown login and possible account takeover.","category":"security","slaTier":"P0","escalate":true,"allowedCitations":["support.sla-and-escalation@1"],"rubricPass":true}, + {"id":"TICKET-006","scenario":"data deletion request","text":"Please permanently delete my account data.","category":"privacy","slaTier":"P1","escalate":true,"allowedCitations":["support.sla-and-escalation@1"],"rubricPass":true}, + {"id":"TICKET-007","scenario":"duplicate ticket","text":"This repeats my open ticket number 4312.","category":"duplicate","slaTier":"P2","escalate":false,"allowedCitations":["support.sla-and-escalation@1"],"rubricPass":true}, + {"id":"TICKET-008","scenario":"abusive message","text":"Your service is useless and your staff are idiots.","category":"abuse","slaTier":"P2","escalate":true,"allowedCitations":["support.sla-and-escalation@1"],"rubricPass":true} +] diff --git a/demo/fixtures/asset-registry/prompt-template.json b/demo/fixtures/asset-registry/prompt-template.json new file mode 100644 index 00000000..21cafdc4 --- /dev/null +++ b/demo/fixtures/asset-registry/prompt-template.json @@ -0,0 +1,110 @@ +{ + "objective": "Classify and draft a safe first response for one L1 support ticket", + "audience": "L1 support agent", + "useWhen": [ + "A new synthetic or customer support ticket is ready for first triage" + ], + "doNotUseWhen": [ + "The ticket contains a legal threat requiring counsel", + "The actor cannot access the approved SLA and escalation source" + ], + "textTemplate": "Using only approved support policy, classify this ticket and return the required JSON object. Ticket: {{ticket_text}}", + "messages": [], + "variables": [ + { + "name": "ticket_text", + "type": "STRING", + "required": true, + "defaultValue": null, + "sensitive": true, + "pattern": "^TICKET-[0-9]{3}: .{3,500}$", + "allowedValues": [] + } + ], + "outputContract": { + "type": "object", + "required": [ + "category", + "slaTier", + "escalate", + "response" + ] + }, + "dataPolicy": { + "retainRawVariables": false, + "retainRawOutput": false + }, + "compatibility": [ + "chat" + ], + "knowledgeRequirements": [ + "support.sla-and-escalation@1" + ], + "evaluationCases": [ + { + "name": "billing question", + "variables": { + "ticket_text": "TICKET-001: Why is my monthly invoice higher than last month?" + }, + "expectedContains": ["billing", "P2", "false"], + "forbiddenContains": ["password", "secret"] + }, + { + "name": "password reset", + "variables": { + "ticket_text": "TICKET-002: I cannot sign in and need a password reset." + }, + "expectedContains": ["access", "P2", "false"], + "forbiddenContains": ["send your password", "secret"] + }, + { + "name": "degraded service", + "variables": { + "ticket_text": "TICKET-003: Reports take ten minutes to load for every user." + }, + "expectedContains": ["degraded_service", "P1", "true"], + "forbiddenContains": ["guarantee"] + }, + { + "name": "confirmed outage", + "variables": { + "ticket_text": "TICKET-004: The production API is unavailable in all regions." + }, + "expectedContains": ["outage", "P0", "true"], + "forbiddenContains": ["root cause is"] + }, + { + "name": "suspected security issue", + "variables": { + "ticket_text": "TICKET-005: I saw an unknown login and possible account takeover." + }, + "expectedContains": ["security", "P0", "true"], + "forbiddenContains": ["share your token"] + }, + { + "name": "data deletion request", + "variables": { + "ticket_text": "TICKET-006: Please permanently delete my account data." + }, + "expectedContains": ["privacy", "P1", "true"], + "forbiddenContains": ["already deleted"] + }, + { + "name": "duplicate ticket", + "variables": { + "ticket_text": "TICKET-007: This repeats my open ticket number 4312." + }, + "expectedContains": ["duplicate", "P2", "false"], + "forbiddenContains": ["closed"] + }, + { + "name": "abusive message", + "variables": { + "ticket_text": "TICKET-008: Your service is useless and your staff are idiots." + }, + "expectedContains": ["abuse", "P2", "true"], + "forbiddenContains": ["insult"] + } + ], + "knownLimitations": "The POC produces a first-response draft only. Refunds, deletion, incident declarations, and security remediation require accountable human approval." +} diff --git a/demo/fixtures/asset-registry/quality-checklist.json b/demo/fixtures/asset-registry/quality-checklist.json new file mode 100644 index 00000000..0251fabd --- /dev/null +++ b/demo/fixtures/asset-registry/quality-checklist.json @@ -0,0 +1,12 @@ +{ + "coordinate": "support.triage-quality@1", + "checks": [ + {"key": "classification", "required": true, "description": "Category matches the ticket intent"}, + {"key": "sla", "required": true, "description": "SLA tier matches approved Knowledge"}, + {"key": "escalation", "required": true, "description": "Escalation decision and accountable team are correct"}, + {"key": "grounding", "required": true, "description": "SLA or escalation claims cite support.sla-and-escalation@1"}, + {"key": "tone", "required": true, "description": "Response is calm, factual, and non-retaliatory"}, + {"key": "schema", "required": true, "description": "Output contains category, slaTier, escalate, and response"}, + {"key": "safety", "required": true, "description": "No secret, unsupported promise, or sensitive raw value is retained"} + ] +} diff --git a/demo/fixtures/asset-registry/success-metrics.json b/demo/fixtures/asset-registry/success-metrics.json new file mode 100644 index 00000000..0d5205be --- /dev/null +++ b/demo/fixtures/asset-registry/success-metrics.json @@ -0,0 +1,13 @@ +{ + "definitions": [ + {"key":"time_to_first_correct_task","formula":"correct_task_completed_at - pack_first_viewed_at","pocThreshold":"captured; no benchmark claim"}, + {"key":"first_time_right","formula":"tasks_passing_without_reviewer_correction / attempted_tasks","pocThreshold":"1.0 for the deterministic golden ticket"}, + {"key":"second_user_reuse","formula":"distinct_non_author_users_with_successful_use","pocThreshold":">= 1"}, + {"key":"view_to_use","formula":"distinct_users_with_use / distinct_users_with_view","pocThreshold":"1.0 in the scripted golden flow"}, + {"key":"evaluation_pass","formula":"passed_evaluation_cases / total_evaluation_cases","pocThreshold":"8 / 8"}, + {"key":"reviewer_correction","formula":"revisions_with_changes_requested / submitted_revisions","pocThreshold":"captured; no benchmark claim"}, + {"key":"owner_coverage","formula":"assets_with_active_owner_and_backup / active_assets","pocThreshold":"1.0 after handover"}, + {"key":"unauthorized_metadata_leakage","formula":"denied_responses_containing_private_asset_metadata","pocThreshold":"0"} + ], + "measurementPolicy": "POC values are technical evidence from deterministic fixtures, not customer adoption benchmarks." +} diff --git a/demo/fixtures/asset-registry/support-sla-and-escalation.md b/demo/fixtures/asset-registry/support-sla-and-escalation.md new file mode 100644 index 00000000..6a93b40f --- /dev/null +++ b/demo/fixtures/asset-registry/support-sla-and-escalation.md @@ -0,0 +1,24 @@ +# L1 Support SLA And Escalation + +Coordinate: `support.sla-and-escalation@1` + +## Response tiers + +- P0: confirmed outage or suspected security issue. Acknowledge within 15 + minutes and immediately escalate to incident response or security. +- P1: degraded service or data-deletion request. Acknowledge within 1 hour and + escalate to the service owner or privacy team. +- P2: billing question, password reset, duplicate ticket, or abusive message. + Acknowledge within 4 business hours. Escalate only when the approved Work + Instruction says so. + +## Safety rules + +- Never request or repeat a password, token, secret, payment-card number, or + unnecessary personal data. +- Never promise a refund, deletion, restoration time, or incident cause before + the accountable team confirms it. +- Cite this source as `support.sla-and-escalation@1` when an SLA or escalation + decision is included. +- Use a calm, factual response for abusive messages and preserve the ticket for + moderator review. diff --git a/demo/fixtures/asset-registry/work-instruction.json b/demo/fixtures/asset-registry/work-instruction.json new file mode 100644 index 00000000..76296c4c --- /dev/null +++ b/demo/fixtures/asset-registry/work-instruction.json @@ -0,0 +1,56 @@ +{ + "purpose": "Classify and respond to one L1 support ticket", + "audience": "L1 support agent", + "prerequisites": [ + "The ticket is assigned to the current agent", + "The agent can access support.sla-and-escalation@1" + ], + "completionOutcome": "The ticket has an approved category, SLA tier, escalation decision, grounded response draft, and audit trace", + "responsibleRole": "L1 support agent", + "steps": [ + { + "key": "inspect", + "title": "Inspect the ticket", + "instruction": "Read only the minimum content needed and identify security, privacy, outage, and abuse signals.", + "expectedResult": "A bounded problem statement without copied secrets", + "check": "No password, token, payment-card number, or unnecessary personal data is copied", + "escalation": "Stop and notify security when credentials or active compromise are present", + "prohibitedActions": ["Request a password", "Paste a token into the Prompt"], + "relatedAssetIds": [], + "relatedKnowledgeVersionIds": [] + }, + { + "key": "ground", + "title": "Check SLA and escalation policy", + "instruction": "Use support.sla-and-escalation@1 to determine the response tier and accountable team.", + "expectedResult": "A P0, P1, or P2 decision with one allowed citation", + "check": "The selected tier and escalation match the approved Knowledge release", + "escalation": "Ask the support operations lead when the policy does not cover the case", + "prohibitedActions": ["Invent an SLA", "Promise an unconfirmed outcome"], + "relatedAssetIds": [], + "relatedKnowledgeVersionIds": [] + }, + { + "key": "draft", + "title": "Render and run the approved Prompt", + "instruction": "Use the exact support.triage-customer-ticket release pinned by the Pack.", + "expectedResult": "A JSON result matching the approved output contract", + "check": "Category, SLA tier, escalation, and response are present", + "escalation": "Do not send output that fails the rubric", + "prohibitedActions": ["Use a draft Prompt", "Silently switch to a newer release"], + "relatedAssetIds": [], + "relatedKnowledgeVersionIds": [] + }, + { + "key": "verify", + "title": "Verify and acknowledge", + "instruction": "Apply support.triage-quality@1, correct any failure, record the final decision, and acknowledge this instruction.", + "expectedResult": "The ticket passes the rubric and the Pack item is complete", + "check": "All required checklist items pass", + "escalation": "Route failed or ambiguous cases to the support operations lead", + "prohibitedActions": ["Hide an evaluation failure", "Mark an inaccessible item complete"], + "relatedAssetIds": [], + "relatedKnowledgeVersionIds": [] + } + ] +} diff --git a/docs/increments/active/README.md b/docs/increments/active/README.md index b1e7a5c5..4c2dc85a 100644 --- a/docs/increments/active/README.md +++ b/docs/increments/active/README.md @@ -15,9 +15,3 @@ progress here. Consolidate current behavior before moving an increment to permission evaluation dataset. 3. Prove the Slack connector against a real workspace, including member removal and the next-crawl access revocation. -4. Validate and execute the - [prompt-first unified Asset Registry program](2026-07-25-unified-asset-registry-definition/plan.md): - pass the architecture/design-partner gate, then land the registry kernel, - authorization, Prompt Template, Work Instruction, Capability Pack, - federated Knowledge, Assistant, generic web, and authenticated read-only MCP - PRs before proving the L1 Support role-onboarding outcome. diff --git a/docs/increments/active/2026-07-25-unified-asset-registry-definition/design.md b/docs/increments/completed/2026-07-25-unified-asset-registry-definition/design.md similarity index 98% rename from docs/increments/active/2026-07-25-unified-asset-registry-definition/design.md rename to docs/increments/completed/2026-07-25-unified-asset-registry-definition/design.md index 1e29a047..4ceca77e 100644 --- a/docs/increments/active/2026-07-25-unified-asset-registry-definition/design.md +++ b/docs/increments/completed/2026-07-25-unified-asset-registry-definition/design.md @@ -553,9 +553,11 @@ Rejected alternatives: - expose every application action as an MCP tool; - implement a generic BPM or Agent runtime. -The named independent debate did not occur. Stakeholder validation with a -support/operations process owner and an AI power user also remains open and is -required before PR 5 can claim the POC is complete. +The named independent debate did not occur. The repository POC closes through +the product-owner accepted deterministic integration and two-session browser +proof in PR 5. No external support-operations stakeholder or customer adoption +validation is claimed; that evidence belongs to a pilot follow-on rather than +being implied by automated acceptance. ## POC Success Gate diff --git a/docs/increments/active/2026-07-25-unified-asset-registry-definition/gate-decisions.md b/docs/increments/completed/2026-07-25-unified-asset-registry-definition/gate-decisions.md similarity index 85% rename from docs/increments/active/2026-07-25-unified-asset-registry-definition/gate-decisions.md rename to docs/increments/completed/2026-07-25-unified-asset-registry-definition/gate-decisions.md index 96bdfe29..a65b14e7 100644 --- a/docs/increments/active/2026-07-25-unified-asset-registry-definition/gate-decisions.md +++ b/docs/increments/completed/2026-07-25-unified-asset-registry-definition/gate-decisions.md @@ -17,11 +17,11 @@ The fixture is synthetic and contains no customer or employee data. | Capability Pack | `support.l1-onboarding@1.0.0` | Ordered required Knowledge, Work Instruction, and Prompt pins | | Evaluation rubric | `support.triage-quality@1` | Classification, SLA, escalation, grounding, tone, and schema checks | -PR 5 will materialize eight deterministic mock tickets: billing question, -password reset, degraded service, confirmed outage, suspected security issue, +PR 5 materializes eight deterministic mock tickets: billing question, password +reset, degraded service, confirmed outage, suspected security issue, data-deletion request, duplicate ticket, and abusive message. Expected labels, SLA tier, escalation decision, allowed citations, and rubric result are pinned -in the fixture. Until then this table is the frozen semantic contract. +under `demo/fixtures/asset-registry`. Two actors prove the flow: @@ -112,6 +112,14 @@ OpenFGA-backed and cannot be replaced by OAuth scope. PR 4 is read-only: no model invocation, progress mutation, review, publication, withdrawal, permission change, or installation. +MCP clients are not modeled as one confidential client per vendor. The +onboarding order is pre-registration when supplied, trusted Client ID Metadata +Documents where supported, then restricted Dynamic Client Registration for +URL-only compatibility. The Keycloak policy forces PKCE S256 and consent, +allows only documented vendor/loopback redirect hosts and `assets:read`, and +bounds anonymous registrations. The `/connect` UI publishes connection +instructions only; Keycloak remains the authorization and consent surface. + Rate limiting is Bucket4j with bounded Caffeine caller state for one POC replica. A multi-replica deployment must move the buckets to a distributed proxy manager; introducing Redis or a generic Spring cache abstraction is not @@ -137,3 +145,13 @@ Caffeine in this PR is private bounded state for the single-node limiter, not a cache of Assets, permissions, evidence, or tokens. The MCP OAuth manager uses a non-persisting authorized-client repository so each request is exchanged against its exact inbound subject token. + +## POC Closure Decision + +Technical closure requires the deterministic integration flow, separate-owner +and second-user browser sessions, opaque denial coverage, full repository +gates, OpenFGA model verification, generated-contract parity, and a terminating +context load. Metrics produced by this fixture are technical acceptance +evidence, not customer adoption benchmarks. Screenpipe, public marketplace, +Skill installation, controlled SOP, executable Workflow/Agent/Tool profiles, +and public MCP mutation remain separate follow-on increments. diff --git a/docs/increments/active/2026-07-25-unified-asset-registry-definition/plan.md b/docs/increments/completed/2026-07-25-unified-asset-registry-definition/plan.md similarity index 88% rename from docs/increments/active/2026-07-25-unified-asset-registry-definition/plan.md rename to docs/increments/completed/2026-07-25-unified-asset-registry-definition/plan.md index 5ee000d5..7d4874e9 100644 --- a/docs/increments/active/2026-07-25-unified-asset-registry-definition/plan.md +++ b/docs/increments/completed/2026-07-25-unified-asset-registry-definition/plan.md @@ -40,9 +40,10 @@ invariants, and their integration tests merely to reduce the file count. - [x] Record the product-owner waiver of the independent Claude Fable 5 debate on 2026-07-25. This is an explicit exception, not a claim that the review ran. -- [ ] Validate the L1 Support onboarding story with one support/operations - process owner and one AI power user. Engineering implementation is authorized - to proceed, but PR 5 cannot claim POC completion until this validation occurs. +- [x] Validate the L1 Support onboarding story through the product-owner + accepted deterministic technical POC: distinct author/reviewer/second-user + integration actors plus separate owner/support-agent browser sessions. + External field validation is not claimed and belongs to the pilot follow-on. - [x] Freeze a small demo-safe fixture: authorized Knowledge, one Work Instruction, one Prompt Template, one Pack, five to ten mock tickets, and one evaluation rubric in `gate-decisions.md`. @@ -50,9 +51,10 @@ invariants, and their integration tests merely to reduce the file count. matrix, retention defaults, and OAuth protected-resource/audience decision in `gate-decisions.md`. -The product owner explicitly authorized PR 1 to start with the named-review -waiver and stakeholder validation still open. That validation remains a hard -completion gate for PR 5. +The product owner explicitly authorized PR 1 with the named-review waiver and +later directed the five-PR sequence through technical completion. PR 5 closes +the repository POC with deterministic acceptance evidence; customer adoption +or external support-operations validation remains a separate pilot outcome. ## PR Dependency Graph @@ -259,7 +261,7 @@ Required gates: - [x] Tool descriptions do not grant authority. - [x] Retrieved Asset content cannot override system/policy instructions. -- [ ] Two-user tests cover recommendation, Pack, Prompt, Knowledge, and +- [x] Two-user tests cover recommendation, Pack, Prompt, Knowledge, and citations. - [x] Every applicable trace contains exact releases without raw secrets. - [x] Assistant has no hidden governance tool path. @@ -267,7 +269,8 @@ Required gates: - [x] Generic detail renders every POC profile without Prompt-specific routing. - [x] Role/permission switch partitions permission-filtered Asset caches by organization, actor, department, and session role. -- [ ] Real-browser author -> review -> release -> second-user Pack journey. +- [x] Real-browser author -> review -> release -> second-user Pack journey + (closed by the PR 5 golden POC). Explicitly excluded: @@ -350,31 +353,38 @@ Expected size: 20-50 files. Scope: -- [ ] Add demo-safe L1 Support Knowledge, Work Instruction, Prompt, checklist, +- [x] Add demo-safe L1 Support Knowledge, Work Instruction, Prompt, checklist, mock tickets, rubric, and Pack fixtures. -- [ ] Prove author -> review -> release -> second-user discovery -> Prompt run +- [x] Prove author -> review -> release -> second-user discovery -> Prompt run -> Work Instruction/Pack completion. -- [ ] Prove one Prompt replacement without silent Pack mutation. -- [ ] Prove withdrawal blocks new use and remains auditable. -- [ ] Prove owner/backup-owner handover and flag orphaned Assets. -- [ ] Capture time-to-first-correct-task, first-time-right, second-user reuse, +- [x] Prove one Prompt replacement without silent Pack mutation. +- [x] Prove withdrawal blocks new use and remains auditable. +- [x] Prove owner/backup-owner handover and flag orphaned Assets. +- [x] Capture time-to-first-correct-task, first-time-right, second-user reuse, view-to-use, evaluation pass, reviewer correction, and owner coverage. -- [ ] Run static, domain, integration, OpenFGA, frontend, browser, MCP, and +- [x] Add generic MCP connection guidance for Claude, Codex, and compatible + clients without storing vendor client secrets in the product UI. +- [x] Enable CIMD and a restricted DCR compatibility fallback with PKCE, + consent, redirect-host and scope allowlists, and a bounded client count. +- [x] Proxy and smoke-test public OAuth protected-resource discovery. +- [x] Run static, domain, integration, OpenFGA, frontend, browser, MCP, and two-user security gates. -- [ ] Consolidate implemented facts into architecture/spec/test/decision docs. -- [ ] Move this increment to `completed` only when every required POC gate has +- [x] Consolidate implemented facts into architecture/spec/test/decision docs. +- [x] Move this increment to `completed` only when every required POC gate has evidence. Required gates: -- [ ] `.\gradlew.bat --no-daemon clean test` -- [ ] OpenFGA model validate and model test -- [ ] generated OpenAPI drift check -- [ ] `corepack pnpm -C web typecheck` -- [ ] `corepack pnpm -C web build` -- [ ] real-browser two-user POC -- [ ] generic denied-resource behavior across REST, Assistant, and MCP -- [ ] terminating context-load test +- [x] `.\gradlew.bat --no-daemon clean test` +- [x] OpenFGA model validate and model test +- [x] generated OpenAPI drift check +- [x] `corepack pnpm -C web typecheck` +- [x] `corepack pnpm -C web build` +- [x] real-browser two-user POC +- [x] generic denied-resource behavior across REST, Assistant, and MCP +- [x] terminating context-load test + +Verification evidence is consolidated in [verification.md](verification.md). ## Follow-On Increments, Not Hidden PRs diff --git a/docs/increments/active/2026-07-25-unified-asset-registry-definition/ui-reference-audit.md b/docs/increments/completed/2026-07-25-unified-asset-registry-definition/ui-reference-audit.md similarity index 100% rename from docs/increments/active/2026-07-25-unified-asset-registry-definition/ui-reference-audit.md rename to docs/increments/completed/2026-07-25-unified-asset-registry-definition/ui-reference-audit.md diff --git a/docs/increments/completed/2026-07-25-unified-asset-registry-definition/verification.md b/docs/increments/completed/2026-07-25-unified-asset-registry-definition/verification.md new file mode 100644 index 00000000..8198c54d --- /dev/null +++ b/docs/increments/completed/2026-07-25-unified-asset-registry-definition/verification.md @@ -0,0 +1,92 @@ +# Asset Registry POC Verification + +Date: 2026-07-26 + +## Golden business flow + +`AssetRegistryIntegrationTests#goldenPocTransfersAReleasedSupportCapabilityToASecondUser` +uses the synthetic files under `demo/fixtures/asset-registry` and proves: + +1. an operations lead authors Prompt, Work Instruction, and Capability Pack + drafts; +2. an independent reviewer approves each immutable digest; +3. exact releases are published and a distinct support agent discovers the + role Pack without receiving an Asset ID; +4. all eight bounded Prompt cases pass with permission-aware Knowledge + grounding; +5. the second user completes one grounded Prompt run, acknowledges the Work + Instruction, and completes the exact-pin Pack; +6. Prompt `2.0.0` does not rewrite the Pack's `1.0.0` pin; +7. withdrawal blocks new use of the old Prompt and leaves an append-only audit + event; +8. active owner/backup assignments produce complete ownership health, while a + missing assignment produces a visible continuity-risk flag. + +`asset-registry-golden-poc.spec.ts` repeats the user-facing proof in separate +real Chromium sessions: the owner sees approved review/release history; the +support agent sees only the permitted Pack, follows the exact release, sees +owner and backup coverage, and reaches 100% progress. + +`mcp-connect.spec.ts` proves that an authenticated user can discover the +canonical MCP URL and follow generic Claude, Codex, or compatible-client +onboarding without receiving a shared client secret. + +## Deterministic POC metrics + +These values are technical acceptance evidence, not customer benchmarks. + +| Metric | POC evidence | +| --- | --- | +| Time to first correct task | start/completion timestamps and Prompt duration are captured; no adoption benchmark claimed | +| First-time-right | 1/1 scripted golden task | +| Second-user reuse | one distinct non-author support agent | +| View-to-use | 1/1 in the scripted browser flow | +| Evaluation pass | 8/8 bounded ticket cases | +| Reviewer correction | 0/4 golden submissions required a correction; the workflow still supports request-changes | +| Owner coverage | 3/3 active golden registry Assets have an owner and backup after handover | +| Unauthorized metadata leakage | zero; REST, Assistant, Pack, and MCP denial tests remain opaque | + +## Gate evidence + +- `.\gradlew.bat --no-daemon clean test` — passed, 97 actionable tasks. +- OpenFGA CLI `v0.7.19`: + - `fga model validate` — `is_valid: true`; + - `fga model test` — 8/8 tests, 66/66 checks, 27/27 ListObjects. +- `corepack pnpm -C web check:api` — generated client matches committed + OpenAPI. +- `corepack pnpm -C web typecheck` — passed. +- `corepack pnpm -C web build` — passed; only the existing chunk-size warning + remains. +- `corepack pnpm -C web test:e2e` — 8/8 Chromium tests passed. +- `OrgMemoryApiContextLoadTests` and `OrgMemoryMcpContextTests` — passed and + terminated cleanly. +- `test-web-forwarded-port.sh` — the public RFC 9728 discovery path is proxied + to MCP rather than falling through to the SPA. +- `test-keycloak-mcp-onboarding.sh` — the checked-in Keycloak 26.7 image: + - imports the minimal production realm and applies the migration twice; + - advertises CIMD plus a DCR registration endpoint; + - creates a public, consent-required, PKCE S256 client with no direct grant, + service account, or full scope; + - rejects an untrusted redirect and `assets:write` with `403`; + - deletes the dynamic verification client. +- Generic denial evidence: + - REST/cross-tenant: + `AssetRegistryIntegrationTests#unauthorizedAndCrossTenantIdsAreOpaqueWhileListIntersectsCanonicalRows`; + - Pack/Assistant: + `CapabilityPackServiceTests` and `AssistantAssetToolServiceTests`; + - MCP: + `AssetDeliveryApiClientTests`, `McpTokenValidationTests`, and + `AssetDeliveryControllerSecurityTests`. + +JetBrains inspection cannot target this feature worktree while the IDE project +is `D:\OrgMemory`. Full backend compile/clean tests plus web lint/typecheck, +generated-contract drift, diff hygiene, and browser gates are the static +fallback. + +## Scope statement + +The repository POC is technically complete. It does not claim customer +adoption or external support-operations stakeholder validation. Screenpipe, +public marketplace, controlled SOP, Skill installation, public MCP mutation, +and executable Workflow/Agent/Tool profiles remain separate follow-on +increments. diff --git a/docs/increments/completed/README.md b/docs/increments/completed/README.md index 57a452dd..eaaa2386 100644 --- a/docs/increments/completed/README.md +++ b/docs/increments/completed/README.md @@ -2,3 +2,8 @@ Completed increments are historical evidence. Current behavior belongs in `ARCHITECTURE.md`, specs, tests, and accepted decisions. + +- [Prompt-first unified Asset Registry POC](2026-07-25-unified-asset-registry-definition/plan.md): + generic governed registry, Prompt/Work Instruction/Pack profiles, federated + Knowledge, Assistant and four web surfaces, authenticated read-only MCP, and + the deterministic two-user L1 Support golden proof. diff --git a/docs/roadmap.md b/docs/roadmap.md index 18000e58..004ec867 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -53,6 +53,12 @@ belongs in one active increment. citations, the permission-aware graph explorer, evaluation harness, and OpenTelemetry-compatible events. Final integration PR #42 is on `main`; remaining live quality/performance evidence belongs to pilot hardening. +- The five-PR + [prompt-first unified Asset Registry POC](increments/completed/2026-07-25-unified-asset-registry-definition/plan.md): + governed generic identity/revision/review/release lifecycle; Prompt Template, + Work Instruction, exact-pin Capability Pack, federated Knowledge; in-app + Assistant and four generic web surfaces; authenticated read-only MCP; and a + deterministic two-user L1 Support golden flow with 8/8 bounded evaluations. ## Active Delivery @@ -87,17 +93,6 @@ belongs in one active increment. 5. Give a Knowledge Space a lifecycle. It can be created and granted at runtime but not retired, and asset movement still needs an explicit retention and authorization contract. -6. Execute the - [prompt-first unified Asset Registry program](increments/active/2026-07-25-unified-asset-registry-definition/plan.md): - pass the independent architecture debate and design-partner gate, then land - the registry kernel, Asset authorization, Prompt Template, Work Instruction, - Capability Pack, federated Knowledge, Assistant, generic web, and authenticated - read-only MCP PRs in dependency order. -7. Prove the first typed Asset outcome: an L1 Support onboarding Pack lets a - second authorized user complete one realistic task with exact released - Knowledge, Work Instruction, and Prompt components; then prove update, - withdrawal, owner handover, audit, and denied-component opacity. - ## Pilot Hardening - S3-compatible production blobs, malware/DLP integration, retention/deletion. diff --git a/docs/runbooks/mcp-asset-delivery.md b/docs/runbooks/mcp-asset-delivery.md index 4729da8f..f9357c0e 100644 --- a/docs/runbooks/mcp-asset-delivery.md +++ b/docs/runbooks/mcp-asset-delivery.md @@ -43,16 +43,41 @@ fresh exchange for a short-lived `orgmemory-web` audience token on every MCP request. Exchanged clients are not persisted by principal name, and the inbound bearer is never forwarded to the API. -For each supported MCP host, pre-register an OAuth client with exact redirect -URIs and assign `assets:read` as an optional scope. Do not enable anonymous -access, wildcard redirects, or unrestricted dynamic client registration for -the POC. A token with only the API audience is rejected by MCP. +Client onboarding is capability-based rather than vendor-specific: + +1. use a pre-registered client when an operator has supplied one; +2. prefer OAuth Client ID Metadata Documents (CIMD) for trusted clients such as + Claude Code and VS Code; +3. use restricted Dynamic Client Registration (DCR) as the compatibility + fallback for URL-only clients such as Claude custom connectors. + +Keycloak is built with the `cimd` feature. Deployment idempotently merges two +client-policy profiles without replacing unrelated realm policies: + +- CIMD accepts HTTPS client IDs only from `claude.ai` and `vscode.dev`, while + metadata redirects may use their documented domains and local loopback; +- anonymous DCR forces PKCE S256, consent, disabled full scope, public clients, + approved redirect/client URI hosts, the standard OIDC `basic` plus + `assets:read` scope allowlist, and a maximum of 50 registered clients. The + migration creates `basic` only when a minimal imported realm does not already + contain it. + +This is restricted anonymous *client registration*, not anonymous Asset +access. Every user still signs in and consents. Never enable wildcard +redirects, confidential-only shared secrets for desktop clients, password +grant, service accounts, or unrestricted DCR. The client-count cap is a POC +abuse bound, not a distributed registration rate limiter; monitor and remove +abandoned dynamic clients before raising it. A token with only the API audience +is rejected by MCP. The checked-in realm files are a baseline for a new Keycloak realm. Keycloak imports with `IGNORE_EXISTING`, so deploying a new image does not mutate an -already-created realm. Before enabling MCP in an existing environment, apply -the same `assets:read` scope/mappers and `orgmemory-mcp` confidential client -through the Keycloak administration path, then verify: +already-created realm. `configure-keycloak-mcp.sh` therefore updates only the +named MCP client policies and anonymous registration-policy components after +Keycloak is healthy; it preserves unrelated client policies, users, +credentials, federation, and clients. The `assets:read` scope/mappers and +confidential `orgmemory-mcp` exchange client must already exist from the realm +baseline or the PR4 migration. Verify: - the incoming MCP token has the MCP URI and `orgmemory-mcp` audiences, but does not have `orgmemory-web`; @@ -71,6 +96,10 @@ GET https://om.kl3in.tech/.well-known/oauth-protected-resource/mcp An unauthenticated `/mcp` request returns `401` and a `WWW-Authenticate` challenge pointing to that metadata URL. +Authenticated users can open `/connect` in OrgMemory for the canonical server +URL and client-specific steps. This page contains no client secret and does not +replace the OAuth consent screen. + ## Authorization And Operations OAuth scopes are coarse admission only. `/api/asset-delivery` resolves the diff --git a/docs/specs/domains/asset-registry.md b/docs/specs/domains/asset-registry.md index 3898cdfe..cde2a342 100644 --- a/docs/specs/domains/asset-registry.md +++ b/docs/specs/domains/asset-registry.md @@ -12,6 +12,11 @@ Consumers always address an exact authorized release. A withdrawn release cannot start new consumption. Forking creates a new Asset draft from an exact release payload and does not copy reviews or approvals. +Every Asset view derives ownership health from active role assignments: +`ownerPresent`, `backupOwnerPresent`, `orphaned`, and `continuityAtRisk`. +Missing ownership coverage is visible in the shared release header; it never +changes release bytes or grants authorization. + ### Prompt Template A Prompt Template release contains exactly one text template or ordered @@ -114,6 +119,20 @@ and applies bounded per-caller plus process-wide rate limits. The confidential MCP gateway exchanges the inbound user token for a short-lived API-audience token; the inbound bearer is never forwarded. +External client onboarding prefers trusted Client ID Metadata Documents and +falls back to restricted Dynamic Client Registration for URL-only clients. +Public clients use Authorization Code with PKCE S256 and user consent; no +vendor-specific secret is shown or stored by the web connection surface. + +### Golden POC Fixture + +`demo/fixtures/asset-registry` is the synthetic, deterministic L1 Support +fixture. It fixes one Knowledge source, one Work Instruction, one Prompt +Template with eight bounded ticket cases, one exact-pin onboarding Pack, one +quality checklist, and metric definitions. The integration proof uses a +distinct author, reviewer, and second user; the real-browser proof uses +separate owner and support-agent sessions. + ## Source Modules - `core.assetregistry` diff --git a/docs/tests/domains/asset-registry.md b/docs/tests/domains/asset-registry.md index 5b904214..a0afd851 100644 --- a/docs/tests/domains/asset-registry.md +++ b/docs/tests/domains/asset-registry.md @@ -29,3 +29,10 @@ | MCP rejects wrong audience, issuer, and expired tokens | `McpTokenValidationTests` | covered | | MCP advertises RFC 9728 metadata and challenges unauthenticated requests with its location | `OrgMemoryMcpContextTests` | covered | | MCP applies bounded per-caller and process-wide token buckets plus known/chunked body limits without returning tokens | `McpRateLimitFilterTests` | covered | +| Public discovery reaches MCP through nginx and deployment smoke requires CIMD/DCR authorization metadata | `test-web-forwarded-port.sh`, `smoke-production.sh` | covered | +| Generic MCP onboarding renders Claude, Codex, and compatible-client instructions without a client secret | `mcp-connect.spec.ts` | covered | +| Golden L1 Support fixture passes eight bounded Prompt cases with permission-aware grounding | `AssetRegistryIntegrationTests#goldenPocTransfersAReleasedSupportCapabilityToASecondUser`, `demo/fixtures/asset-registry` | covered | +| Second-user discovery, exact-release Prompt use, Work Instruction acknowledgement, Pack completion, replacement pin stability, and withdrawal are one integrated flow | `AssetRegistryIntegrationTests#goldenPocTransfersAReleasedSupportCapabilityToASecondUser` | covered | +| Active owner/backup coverage derives an explicit orphaned and continuity-risk flag | `AssetRegistryIntegrationTests#goldenPocTransfersAReleasedSupportCapabilityToASecondUser`, `AssetIdentityHeader` | covered | +| Owner governance and second-user Pack completion render in separate real browser sessions | `asset-registry-golden-poc.spec.ts` | covered | +| Generic denial stays opaque across REST, Assistant, and MCP | `AssetRegistryIntegrationTests#unauthorizedAndCrossTenantIdsAreOpaqueWhileListIntersectsCanonicalRows`, `AssetDeliveryApiClientTests`, `CapabilityPackServiceTests` | covered | diff --git a/docs/vision.md b/docs/vision.md index fc0ba9a9..6fe3de5e 100644 --- a/docs/vision.md +++ b/docs/vision.md @@ -187,16 +187,15 @@ bypass authorization. ## Web Direction -The current registry UI is disposable prototype evidence. The replacement is an -agent-first workspace centered on one Asset Registry with four generic -surfaces: **For you / Asset catalog**, **Asset detail / use**, **Pack journey**, -and **Governance workspace**. Asset type profiles supply their renderer and -actions; the product must not hard-code a Prompt-only page hierarchy. Search, -ask, citations, release history, provenance, permissions, source health, and -later Skill installation reuse that shell. A Skill Registry is a filtered -installable view of the shared catalog, not another lifecycle. Reuse shadcn/ui -and maintained libraries, keep light and dark themes, and avoid porting old -page composition merely for parity. +The POC implements an agent-first workspace centered on one Asset Registry with +four generic surfaces: **For you / Asset catalog**, **Asset detail / use**, +**Pack journey**, and **Governance workspace**. Asset type profiles supply +their renderer and actions; the product does not hard-code a Prompt-only page +hierarchy. Search, ask, citations, release history, provenance, permissions, +source health, and later Skill installation reuse that shell. A Skill Registry +is a filtered installable view of the shared catalog, not another lifecycle. +Production UX may evolve from this evidence, while retaining the same +permission and exact-release contracts. ## Non-Goals For The First Pilot diff --git a/infrastructure/deployment/scripts/configure-keycloak-mcp.sh b/infrastructure/deployment/scripts/configure-keycloak-mcp.sh new file mode 100755 index 00000000..546497bf --- /dev/null +++ b/infrastructure/deployment/scripts/configure-keycloak-mcp.sh @@ -0,0 +1,208 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" +compose_file="$repo_root/infrastructure/deployment/compose.production.yaml" +environment_file="${ORGMEMORY_ENV_FILE:-$repo_root/.env.production}" +realm="${ORGMEMORY_KEYCLOAK_REALM:-orgmemory}" +keycloak_container="${ORGMEMORY_KEYCLOAK_CONTAINER:-}" +profiles_source="$repo_root/infrastructure/keycloak/mcp-client-profiles.json" +policies_source="$repo_root/infrastructure/keycloak/mcp-client-policies.json" +registration_policy_source="$repo_root/infrastructure/keycloak/mcp-dcr-registration-policy.json" +basic_scope_source="$repo_root/infrastructure/keycloak/mcp-basic-client-scope.json" +kcadm_config="/tmp/orgmemory-mcp-kcadm.config" +tmp_root="${TMPDIR:-/tmp}" +tmp_dir="$(mktemp -d "${tmp_root%/}/orgmemory-keycloak-mcp.XXXXXX")" + +compose=( + docker compose + --file "$compose_file" + --env-file "$environment_file" +) + +keycloak_exec() { + if [[ -n "$keycloak_container" ]]; then + MSYS_NO_PATHCONV=1 docker exec -i "$keycloak_container" "$@" + else + "${compose[@]}" exec -T keycloak "$@" + fi +} + +kcadm() { + keycloak_exec \ + /opt/keycloak/bin/kcadm.sh "$@" --config "$kcadm_config" +} + +cleanup() { + keycloak_exec rm -f "$kcadm_config" >/dev/null 2>&1 || true + case "$tmp_dir" in + "${tmp_root%/}"/orgmemory-keycloak-mcp.*) + rm -rf -- "$tmp_dir" + ;; + *) + printf 'Refusing to remove unexpected temporary path: %s\n' "$tmp_dir" >&2 + ;; + esac +} +trap cleanup EXIT + +# The quoted variables are intentionally expanded inside the Keycloak container. +# shellcheck disable=SC2016 +keycloak_exec bash -ec \ + '/opt/keycloak/bin/kcadm.sh config credentials \ + --config /tmp/orgmemory-mcp-kcadm.config \ + --server http://127.0.0.1:8080 \ + --realm master \ + --user "$KC_BOOTSTRAP_ADMIN_USERNAME" \ + --password "$KC_BOOTSTRAP_ADMIN_PASSWORD" >/dev/null' + +kcadm get "realms/$realm" >/dev/null + +client_scopes_csv="$tmp_dir/client-scopes.csv" +kcadm get client-scopes \ + -r "$realm" \ + --fields id,name \ + --format csv \ + --noquotes \ + | tr -d '\r' >"$client_scopes_csv" +if ! awk -F, '$2 == "basic" { found = 1 } END { exit !found }' \ + "$client_scopes_csv"; then + kcadm create client-scopes -r "$realm" -f - <"$basic_scope_source" >/dev/null +fi + +merge_client_policy_document() { + local endpoint="$1" + local array_key="$2" + local desired_source="$3" + local current_path="$tmp_dir/${array_key}-current.json" + local merged_path="$tmp_dir/${array_key}-merged.json" + + kcadm get "$endpoint" -r "$realm" >"$current_path" + python3 - "$current_path" "$desired_source" "$array_key" >"$merged_path" <<'PY' +import json +import sys + +current_path, desired_path, array_key = sys.argv[1:] +with open(current_path, encoding="utf-8") as stream: + current = json.load(stream) +with open(desired_path, encoding="utf-8") as stream: + desired = json.load(stream) + +desired_items = desired[array_key] +desired_names = {item["name"] for item in desired_items} +preserved = [ + item for item in current.get(array_key, []) + if item.get("name") not in desired_names +] +json.dump({array_key: [*preserved, *desired_items]}, sys.stdout) +PY + kcadm update "$endpoint" -r "$realm" -f - <"$merged_path" +} + +merge_client_policy_document \ + client-policies/profiles profiles "$profiles_source" +merge_client_policy_document \ + client-policies/policies policies "$policies_source" + +components_csv="$tmp_dir/registration-components.csv" +kcadm get components \ + -r "$realm" \ + -q type=org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy \ + --fields id,providerId,subType \ + --format csv \ + --noquotes \ + | tr -d '\r' >"$components_csv" + +mapfile -t registration_providers < <( + python3 - "$registration_policy_source" <<'PY' | tr -d '\r' +import json +import sys + +with open(sys.argv[1], encoding="utf-8") as stream: + for provider_id in json.load(stream): + print(provider_id) +PY +) + +for provider_id in "${registration_providers[@]}"; do + component_id="$( + awk -F, -v provider="$provider_id" \ + '$2 == provider && $3 == "anonymous" { print $1; exit }' \ + "$components_csv" + )" + if [[ -z "$component_id" ]]; then + printf 'Missing anonymous Keycloak registration policy: %s\n' "$provider_id" >&2 + exit 1 + fi + + case "$provider_id" in + trusted-hosts) + kcadm update "components/$component_id" -r "$realm" \ + -s 'config."host-sending-registration-request-must-match"=["false"]' \ + -s 'config."trusted-hosts"=["localhost","127.0.0.1","claude.ai","claude.com","vscode.dev"]' \ + -s 'config."client-uris-must-match"=["true"]' + ;; + allowed-client-templates) + kcadm update "components/$component_id" -r "$realm" \ + -s 'config."allow-default-scopes"=["true"]' \ + -s 'config."allowed-client-scopes"=["basic","assets:read"]' + ;; + max-clients) + kcadm update "components/$component_id" -r "$realm" \ + -s 'config."max-clients"=["50"]' + ;; + *) + printf 'Unsupported MCP registration policy: %s\n' "$provider_id" >&2 + exit 1 + ;; + esac + + component_path="$tmp_dir/component-${provider_id}.json" + kcadm get "components/$component_id" -r "$realm" >"$component_path" + python3 \ + - "$component_path" "$registration_policy_source" "$provider_id" <<'PY' +import json +import sys + +component_path, policy_path, provider_id = sys.argv[1:] +with open(component_path, encoding="utf-8") as stream: + actual = json.load(stream)["config"] +with open(policy_path, encoding="utf-8") as stream: + expected = json.load(stream)[provider_id] + +normalize = lambda config: { + key: sorted(values) for key, values in config.items() +} +if normalize(actual) != normalize(expected): + raise SystemExit( + f"Keycloak registration policy verification failed for {provider_id}" + ) +PY +done + +profiles_actual="$tmp_dir/profiles-actual.json" +policies_actual="$tmp_dir/policies-actual.json" +kcadm get client-policies/profiles -r "$realm" >"$profiles_actual" +kcadm get client-policies/policies -r "$realm" >"$policies_actual" + +python3 \ + - "$profiles_actual" "$profiles_source" profiles \ + "$policies_actual" "$policies_source" policies <<'PY' +import json +import sys + +for offset in (1, 4): + actual_path, expected_path, array_key = sys.argv[offset:offset + 3] + with open(actual_path, encoding="utf-8") as stream: + actual = json.load(stream) + with open(expected_path, encoding="utf-8") as stream: + expected = json.load(stream) + actual_by_name = {item["name"]: item for item in actual[array_key]} + for item in expected[array_key]: + if actual_by_name.get(item["name"]) != item: + raise SystemExit( + f"Keycloak {array_key} verification failed for {item['name']}" + ) +PY + +printf 'Keycloak MCP client onboarding policies are configured.\n' diff --git a/infrastructure/deployment/scripts/deploy.sh b/infrastructure/deployment/scripts/deploy.sh index 7b0b042c..d98524e2 100755 --- a/infrastructure/deployment/scripts/deploy.sh +++ b/infrastructure/deployment/scripts/deploy.sh @@ -153,6 +153,9 @@ compose=( --wait-timeout 240 \ --remove-orphans +ORGMEMORY_ENV_FILE="$environment_file" \ + "$repo_root/infrastructure/deployment/scripts/configure-keycloak-mcp.sh" + ORGMEMORY_ENV_FILE="$environment_file" \ ORGMEMORY_REQUIRE_PUBLIC_SMOKE="${public_smoke:-true}" \ "$repo_root/infrastructure/deployment/scripts/smoke-production.sh" diff --git a/infrastructure/deployment/scripts/smoke-production.sh b/infrastructure/deployment/scripts/smoke-production.sh index fc9eef56..65bab7bd 100755 --- a/infrastructure/deployment/scripts/smoke-production.sh +++ b/infrastructure/deployment/scripts/smoke-production.sh @@ -61,6 +61,66 @@ if [[ "${ORGMEMORY_REQUIRE_PUBLIC_SMOKE:-false}" == "true" ]]; then https://auth.kl3in.tech/realms/orgmemory/.well-known/openid-configuration \ | python3 -c 'import json,sys; print(json.load(sys.stdin)["issuer"])')" [[ "$issuer" == "https://auth.kl3in.tech/realms/orgmemory" ]] + + protected_resource_metadata="$( + curl --fail --silent --show-error \ + --connect-timeout 5 \ + --max-time 15 \ + --retry 5 \ + --retry-all-errors \ + --retry-delay 2 \ + https://om.kl3in.tech/.well-known/oauth-protected-resource/mcp + )" + python3 -c ' +import json +import sys + +document = json.load(sys.stdin) +assert document["resource"] == "https://om.kl3in.tech/mcp" +assert document["authorization_servers"] == [ + "https://auth.kl3in.tech/realms/orgmemory" +] +assert "assets:read" in document["scopes_supported"] +' <<<"$protected_resource_metadata" + + authorization_metadata="$( + curl --fail --silent --show-error \ + --connect-timeout 5 \ + --max-time 15 \ + --retry 5 \ + --retry-all-errors \ + --retry-delay 2 \ + https://auth.kl3in.tech/realms/orgmemory/.well-known/oauth-authorization-server + )" + python3 -c ' +import json +import sys + +document = json.load(sys.stdin) +assert document["registration_endpoint"].endswith("/clients-registrations/openid-connect") +assert document.get("client_id_metadata_document_supported") is True +' <<<"$authorization_metadata" + + challenge_headers="$(mktemp)" + trap 'rm -f "$challenge_headers"' EXIT + mcp_status="$( + curl --silent --show-error \ + --connect-timeout 5 \ + --max-time 15 \ + --retry 5 \ + --retry-all-errors \ + --retry-delay 2 \ + --dump-header "$challenge_headers" \ + --output /dev/null \ + --write-out '%{http_code}' \ + https://om.kl3in.tech/mcp + )" + [[ "$mcp_status" == "401" ]] + grep -Eiq \ + '^[[:space:]]*www-authenticate:.*resource_metadata="https://om\.kl3in\.tech/\.well-known/oauth-protected-resource/mcp"' \ + "$challenge_headers" + rm -f "$challenge_headers" + trap - EXIT fi printf 'OrgMemory production smoke passed.\n' diff --git a/infrastructure/deployment/scripts/test-keycloak-mcp-onboarding.sh b/infrastructure/deployment/scripts/test-keycloak-mcp-onboarding.sh new file mode 100755 index 00000000..cffb2089 --- /dev/null +++ b/infrastructure/deployment/scripts/test-keycloak-mcp-onboarding.sh @@ -0,0 +1,200 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" +run_id="${RANDOM}-$$" +container="orgmemory-keycloak-mcp-test-${run_id}" +image="orgmemory-keycloak-mcp-test:${run_id}" +tmp_root="${TMPDIR:-/tmp}" +tmp_dir="$(mktemp -d "${tmp_root%/}/orgmemory-keycloak-mcp-test.XXXXXX")" + +cleanup() { + if [[ "$(docker inspect "$container" --format '{{.Name}}' 2>/dev/null || true)" == "/$container" ]]; then + docker rm --force "$container" >/dev/null + fi + docker image rm "$image" >/dev/null 2>&1 || true + case "$tmp_dir" in + "${tmp_root%/}"/orgmemory-keycloak-mcp-test.*) + rm -rf -- "$tmp_dir" + ;; + *) + printf 'Refusing to remove unexpected temporary path: %s\n' "$tmp_dir" >&2 + ;; + esac +} +trap cleanup EXIT + +docker build \ + --tag "$image" \ + --file "$repo_root/infrastructure/keycloak/Dockerfile" \ + "$repo_root" >/dev/null + +docker run --detach \ + --name "$container" \ + --publish 127.0.0.1::8080 \ + --env KC_DB=dev-file \ + --env KC_BOOTSTRAP_ADMIN_USERNAME=admin \ + --env KC_BOOTSTRAP_ADMIN_PASSWORD=verification-only \ + --env ORGMEMORY_OIDC_CLIENT_SECRET=verification-web-only \ + --env ORGMEMORY_MCP_OIDC_CLIENT_SECRET=verification-mcp-only \ + --env ORGMEMORY_MCP_RESOURCE_URI=https://om.kl3in.tech/mcp \ + "$image" \ + start-dev --import-realm >/dev/null + +port="$( + docker port "$container" 8080/tcp \ + | tail -n 1 \ + | sed -E 's/.*:([0-9]+)$/\1/' +)" +base_url="http://127.0.0.1:${port}" +metadata_url="$base_url/realms/orgmemory/.well-known/oauth-authorization-server" + +ready=false +for _ in {1..90}; do + if curl --fail --silent --show-error \ + --connect-timeout 2 \ + --max-time 3 \ + "$metadata_url" >"$tmp_dir/metadata.json" 2>/dev/null; then + ready=true + break + fi + sleep 1 +done +if [[ "$ready" != "true" ]]; then + docker logs "$container" >&2 + exit 1 +fi + +ORGMEMORY_KEYCLOAK_CONTAINER="$container" \ +ORGMEMORY_KEYCLOAK_REALM=orgmemory \ + "$repo_root/infrastructure/deployment/scripts/configure-keycloak-mcp.sh" +ORGMEMORY_KEYCLOAK_CONTAINER="$container" \ +ORGMEMORY_KEYCLOAK_REALM=orgmemory \ + "$repo_root/infrastructure/deployment/scripts/configure-keycloak-mcp.sh" + +curl --fail --silent --show-error "$metadata_url" >"$tmp_dir/metadata.json" +python3 - "$tmp_dir/metadata.json" <<'PY' +import json +import sys + +with open(sys.argv[1], encoding="utf-8") as stream: + metadata = json.load(stream) +assert metadata["client_id_metadata_document_supported"] is True +assert metadata["registration_endpoint"].endswith( + "/clients-registrations/openid-connect" +) +PY + +registration_url="$base_url/realms/orgmemory/clients-registrations/openid-connect" +cat >"$tmp_dir/good-client.json" <<'JSON' +{ + "client_name": "OrgMemory deployment contract", + "redirect_uris": ["http://127.0.0.1:43821/oauth/callback"], + "grant_types": ["authorization_code", "refresh_token"], + "response_types": ["code"], + "token_endpoint_auth_method": "none", + "scope": "assets:read" +} +JSON +curl --fail --silent --show-error \ + --request POST \ + --header 'Content-Type: application/json' \ + --data-binary "@$tmp_dir/good-client.json" \ + "$registration_url" >"$tmp_dir/registration.json" + +client_id="$( + python3 -c \ + 'import json,sys; print(json.load(open(sys.argv[1], encoding="utf-8"))["client_id"])' \ + "$tmp_dir/registration.json" +)" +MSYS_NO_PATHCONV=1 docker exec "$container" sh -ec \ + '/opt/keycloak/bin/kcadm.sh config credentials \ + --config /tmp/orgmemory-mcp-test.config \ + --server http://127.0.0.1:8080 \ + --realm master \ + --user "$KC_BOOTSTRAP_ADMIN_USERNAME" \ + --password "$KC_BOOTSTRAP_ADMIN_PASSWORD" >/dev/null' +MSYS_NO_PATHCONV=1 docker exec "$container" \ + /opt/keycloak/bin/kcadm.sh get clients \ + -r orgmemory \ + -q "clientId=$client_id" \ + --config /tmp/orgmemory-mcp-test.config >"$tmp_dir/client.json" +python3 - "$tmp_dir/client.json" <<'PY' +import json +import sys + +with open(sys.argv[1], encoding="utf-8") as stream: + clients = json.load(stream) +assert len(clients) == 1 +client = clients[0] +assert client["publicClient"] is True +assert client["consentRequired"] is True +assert client["fullScopeAllowed"] is False +assert client["directAccessGrantsEnabled"] is False +assert client["serviceAccountsEnabled"] is False +assert client["attributes"]["pkce.code.challenge.method"] == "S256" +PY + +cat >"$tmp_dir/bad-redirect.json" <<'JSON' +{ + "client_name": "Rejected redirect", + "redirect_uris": ["https://evil.example/callback"], + "grant_types": ["authorization_code"], + "response_types": ["code"], + "token_endpoint_auth_method": "none", + "scope": "assets:read" +} +JSON +bad_redirect_status="$( + curl --silent --show-error \ + --output /dev/null \ + --write-out '%{http_code}' \ + --request POST \ + --header 'Content-Type: application/json' \ + --data-binary "@$tmp_dir/bad-redirect.json" \ + "$registration_url" +)" +[[ "$bad_redirect_status" == "403" ]] + +cat >"$tmp_dir/bad-scope.json" <<'JSON' +{ + "client_name": "Rejected scope", + "redirect_uris": ["http://127.0.0.1:43822/oauth/callback"], + "grant_types": ["authorization_code"], + "response_types": ["code"], + "token_endpoint_auth_method": "none", + "scope": "assets:write" +} +JSON +bad_scope_status="$( + curl --silent --show-error \ + --output /dev/null \ + --write-out '%{http_code}' \ + --request POST \ + --header 'Content-Type: application/json' \ + --data-binary "@$tmp_dir/bad-scope.json" \ + "$registration_url" +)" +[[ "$bad_scope_status" == "403" ]] + +registration_uri="$( + python3 -c \ + 'import json,sys; print(json.load(open(sys.argv[1], encoding="utf-8"))["registration_client_uri"])' \ + "$tmp_dir/registration.json" +)" +registration_token="$( + python3 -c \ + 'import json,sys; print(json.load(open(sys.argv[1], encoding="utf-8"))["registration_access_token"])' \ + "$tmp_dir/registration.json" +)" +delete_status="$( + curl --silent --show-error \ + --output /dev/null \ + --write-out '%{http_code}' \ + --request DELETE \ + --header "Authorization: Bearer $registration_token" \ + "$registration_uri" +)" +[[ "$delete_status" == "204" ]] + +printf 'Keycloak MCP onboarding contract passed.\n' diff --git a/infrastructure/deployment/scripts/test-web-forwarded-port.sh b/infrastructure/deployment/scripts/test-web-forwarded-port.sh index 895d4547..04d8ce29 100755 --- a/infrastructure/deployment/scripts/test-web-forwarded-port.sh +++ b/infrastructure/deployment/scripts/test-web-forwarded-port.sh @@ -35,6 +35,7 @@ trap cleanup EXIT cat >"$tmp_dir/api.conf" <<'NGINX' server { listen 8080; + listen 8090; server_name _; location / { @@ -50,6 +51,7 @@ docker run --detach --rm \ --name "$api_container" \ --network "$network" \ --network-alias api \ + --network-alias mcp \ --volume "$api_config_path:/etc/nginx/conf.d/default.conf:ro" \ "$runtime_image" >/dev/null @@ -96,4 +98,14 @@ assert_forwarded_port 8443 \ --header="X-Forwarded-Proto: https" \ --header="X-Forwarded-Port: 8443" +discovery_headers="$( + docker exec "$web_container" \ + wget -S -O /dev/null \ + --header="X-Forwarded-Proto: https" \ + http://127.0.0.1:8080/.well-known/oauth-protected-resource/mcp 2>&1 +)" +grep -Eiq \ + '^[[:space:]]*X-Seen-Forwarded-Port:[[:space:]]*443[[:space:]]*$' \ + <<<"$discovery_headers" + printf 'Web forwarded-port regression passed.\n' diff --git a/infrastructure/keycloak/Dockerfile b/infrastructure/keycloak/Dockerfile index aae60b40..3c021586 100644 --- a/infrastructure/keycloak/Dockerfile +++ b/infrastructure/keycloak/Dockerfile @@ -5,6 +5,7 @@ ARG KEYCLOAK_IMAGE=quay.io/keycloak/keycloak:26.7.0@sha256:0f198be292568439d700c FROM ${KEYCLOAK_IMAGE} AS build ENV KC_DB=postgres \ + KC_FEATURES=cimd \ KC_HEALTH_ENABLED=true \ KC_METRICS_ENABLED=true @@ -24,6 +25,7 @@ COPY --from=build /opt/keycloak/ /opt/keycloak/ COPY --chown=keycloak:keycloak infrastructure/keycloak/orgmemory-realm.prod.json /opt/keycloak/data/import/orgmemory-realm.json ENV KC_DB=postgres \ + KC_FEATURES=cimd \ KC_HEALTH_ENABLED=true \ KC_METRICS_ENABLED=true diff --git a/infrastructure/keycloak/mcp-basic-client-scope.json b/infrastructure/keycloak/mcp-basic-client-scope.json new file mode 100644 index 00000000..a07e53cc --- /dev/null +++ b/infrastructure/keycloak/mcp-basic-client-scope.json @@ -0,0 +1,35 @@ +{ + "name": "basic", + "description": "OpenID Connect scope for basic subject and authentication-time claims", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "name": "auth_time", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "AUTH_TIME", + "id.token.claim": "true", + "introspection.token.claim": "true", + "access.token.claim": "true", + "claim.name": "auth_time", + "jsonType.label": "long" + } + }, + { + "name": "sub", + "protocol": "openid-connect", + "protocolMapper": "oidc-sub-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "access.token.claim": "true" + } + } + ] +} diff --git a/infrastructure/keycloak/mcp-client-policies.json b/infrastructure/keycloak/mcp-client-policies.json new file mode 100644 index 00000000..ccaef575 --- /dev/null +++ b/infrastructure/keycloak/mcp-client-policies.json @@ -0,0 +1,45 @@ +{ + "policies": [ + { + "name": "orgmemory-mcp-cimd-policy", + "description": "Allow trusted MCP Client ID Metadata Documents", + "enabled": true, + "conditions": [ + { + "condition": "client-id-uri", + "configuration": { + "client-id-uri-allow-permitted-domains": [ + "claude.ai", + "vscode.dev" + ], + "client-id-uri-scheme": [ + "https" + ] + } + } + ], + "profiles": [ + "orgmemory-mcp-cimd-profile" + ] + }, + { + "name": "orgmemory-mcp-dcr-policy", + "description": "Apply hardened OAuth defaults to anonymous MCP DCR clients", + "enabled": true, + "conditions": [ + { + "condition": "client-updater-context", + "configuration": { + "update-client-source": [ + "ByAnonymous", + "ByRegistrationAccessToken" + ] + } + } + ], + "profiles": [ + "orgmemory-mcp-dcr-profile" + ] + } + ] +} diff --git a/infrastructure/keycloak/mcp-client-profiles.json b/infrastructure/keycloak/mcp-client-profiles.json new file mode 100644 index 00000000..a84e9d50 --- /dev/null +++ b/infrastructure/keycloak/mcp-client-profiles.json @@ -0,0 +1,37 @@ +{ + "profiles": [ + { + "name": "orgmemory-mcp-cimd-profile", + "description": "Trusted MCP clients using OAuth Client ID Metadata Documents", + "executors": [ + { + "executor": "client-id-metadata-document", + "configuration": { + "cimd-allow-http-scheme": false, + "only-allow-confidential-client": false, + "cimd-allow-permitted-domains": [ + "claude.ai", + "localhost", + "127.0.0.1", + "vscode.dev", + "code.visualstudio.com" + ], + "cimd-restrict-same-domain": false + } + } + ] + }, + { + "name": "orgmemory-mcp-dcr-profile", + "description": "Require PKCE S256 for anonymously registered MCP clients", + "executors": [ + { + "executor": "pkce-enforcer", + "configuration": { + "auto-configure": true + } + } + ] + } + ] +} diff --git a/infrastructure/keycloak/mcp-dcr-registration-policy.json b/infrastructure/keycloak/mcp-dcr-registration-policy.json new file mode 100644 index 00000000..62977147 --- /dev/null +++ b/infrastructure/keycloak/mcp-dcr-registration-policy.json @@ -0,0 +1,31 @@ +{ + "trusted-hosts": { + "host-sending-registration-request-must-match": [ + "false" + ], + "trusted-hosts": [ + "localhost", + "127.0.0.1", + "claude.ai", + "claude.com", + "vscode.dev" + ], + "client-uris-must-match": [ + "true" + ] + }, + "allowed-client-templates": { + "allow-default-scopes": [ + "true" + ], + "allowed-client-scopes": [ + "basic", + "assets:read" + ] + }, + "max-clients": { + "max-clients": [ + "50" + ] + } +} diff --git a/web/nginx.conf b/web/nginx.conf index 3ed4370f..89cc4ab5 100644 --- a/web/nginx.conf +++ b/web/nginx.conf @@ -94,6 +94,18 @@ server { add_header X-Accel-Buffering no; } + location = /.well-known/oauth-protected-resource/mcp { + proxy_pass $mcp_upstream$request_uri; + proxy_http_version 1.1; + proxy_set_header Host $http_host; + proxy_set_header X-Forwarded-Host $http_host; + proxy_set_header X-Forwarded-Port $forwarded_port; + proxy_set_header X-Forwarded-Proto $forwarded_proto; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_read_timeout 30s; + } + location / { try_files $uri $uri/ /index.html; } diff --git a/web/src/components/app-shell/app-sidebar.tsx b/web/src/components/app-shell/app-sidebar.tsx index 389f11a4..78a322a3 100644 --- a/web/src/components/app-shell/app-sidebar.tsx +++ b/web/src/components/app-shell/app-sidebar.tsx @@ -1,4 +1,4 @@ -import { Boxes, Files, MessageSquareText, Network, UserRoundCog } from "lucide-react" +import { Boxes, Files, MessageSquareText, Network, Plug, UserRoundCog } from "lucide-react" import { Link, useLocation } from "@tanstack/react-router" import { AccountMenu } from "@/components/app-shell/account-menu" @@ -24,6 +24,7 @@ const NAVIGATION = [ { label: "Assistant", to: "/" as const, icon: MessageSquareText }, { label: "Assets", to: "/assets" as const, icon: Boxes }, { label: "Documents", to: "/sources" as const, icon: Files }, + { label: "Connect", to: "/connect" as const, icon: Plug }, ] const ITEM_CLASSES = diff --git a/web/src/features/assets/components/asset-detail-page.tsx b/web/src/features/assets/components/asset-detail-page.tsx index 0da2b092..f5801dd5 100644 --- a/web/src/features/assets/components/asset-detail-page.tsx +++ b/web/src/features/assets/components/asset-detail-page.tsx @@ -191,6 +191,15 @@ function AssetIdentityHeader({
{meta.label} {asset.portfolioState} + {asset.ownershipHealth?.orphaned ? ( + + Orphaned + + ) : asset.ownershipHealth?.continuityAtRisk ? ( + + Ownership gap + + ) : null} {release?.availability === "DEPRECATED" ? ( Deprecated diff --git a/web/src/features/mcp/components/mcp-connect-page.tsx b/web/src/features/mcp/components/mcp-connect-page.tsx new file mode 100644 index 00000000..71eb8beb --- /dev/null +++ b/web/src/features/mcp/components/mcp-connect-page.tsx @@ -0,0 +1,282 @@ +import { Check, Copy, ExternalLink, LockKeyhole, Plug, ShieldCheck } from "lucide-react" +import { toast } from "sonner" + +import { Badge } from "@/components/ui/badge" +import { Button } from "@/components/ui/button" +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card" +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" + +const MCP_ENDPOINT = "https://om.kl3in.tech/mcp" +const CLAUDE_CODE_COMMAND = + "claude mcp add --transport http orgmemory https://om.kl3in.tech/mcp" +const CODEX_ADD_COMMAND = + "codex mcp add orgmemory --url https://om.kl3in.tech/mcp --oauth-resource https://om.kl3in.tech/mcp" +const CODEX_LOGIN_COMMAND = "codex mcp login orgmemory --scopes assets:read" + +export function McpConnectPage() { + return ( +
+
+
+
+ + +
+

Connect AI clients

+

+ Use released OrgMemory Assets from your preferred assistant without + copying content or credentials between products. +

+
+
+
+ + + +
+ + + Read only + OAuth 2.1 +
+ OrgMemory MCP + + One Streamable HTTP endpoint for Claude, Codex, and compatible MCP + clients. + +
+ + + +
+ + + + + Claude + + + Codex + + + Other clients + + + + + + + Open Settings → Connectors, add a custom connector, + and paste the server URL above. Choose Connect and + approve the read-only access request. + + + Run this once, then use /mcp inside Claude Code to sign + in and inspect the connection. + + + + + + + + + + + + + Codex opens your browser for OrgMemory consent. The token remains in + the client credential store. + + + + + + + + + Select Streamable HTTP and enter the server URL. + + + Use OAuth 2.1 Authorization Code with PKCE. OrgMemory supports Client + ID Metadata Documents for trusted clients and restricted Dynamic + Client Registration as a compatibility fallback. + + + Request assets:read. OrgMemory still checks your live + access to every Asset, Pack item, and release. + +

+ Loopback callbacks are supported for local clients. A hosted client + with another callback domain needs an administrator to approve that + domain first. +

+
+
+
+ + + + + + + Search authorized Assets, read exact releases and Pack contents, resolve + relations, and render released Prompt templates with variables. + + + + + + + +
+
+ ) +} + +function ClientCard({ + title, + description, + children, +}: { + title: string + description: string + children: React.ReactNode +}) { + return ( + + + {title} + {description} + + {children} + + ) +} + +function SetupStep({ + number, + title, + children, +}: { + number: number + title: string + children: React.ReactNode +}) { + return ( +
+ + {number} + +
+

{title}

+
+ {children} +
+
+
+ ) +} + +function CopyField({ label, value }: { label: string; value: string }) { + return ( +
+

+ {label} +

+
+ + {value} + + +
+
+ ) +} + +function CommandBlock({ command }: { command: string }) { + return ( +
+ + {command} + + +
+ ) +} + +function CopyButton({ value, label }: { value: string; label: string }) { + return ( + + ) +} + +function SecurityPoint({ + icon: Icon, + title, + body, +}: { + icon: typeof ShieldCheck + title: string + body: string +}) { + return ( +
+
+ ) +} diff --git a/web/src/routeTree.gen.ts b/web/src/routeTree.gen.ts index 71cb5798..ef871ac4 100644 --- a/web/src/routeTree.gen.ts +++ b/web/src/routeTree.gen.ts @@ -13,6 +13,7 @@ import { Route as AuthenticatedRouteImport } from './routes/_authenticated' import { Route as AdminRouteImport } from './routes/admin' import { Route as LoginRouteImport } from './routes/login' import { Route as AuthenticatedIndexRouteImport } from './routes/_authenticated/index' +import { Route as AuthenticatedConnectRouteImport } from './routes/_authenticated/connect' import { Route as AuthenticatedSourcesRouteImport } from './routes/_authenticated/sources' import { Route as AdminIndexRouteImport } from './routes/admin/index' import { Route as AdminAccessRouteImport } from './routes/admin/access' @@ -50,6 +51,11 @@ const AuthenticatedIndexRoute = AuthenticatedIndexRouteImport.update({ path: '/', getParentRoute: () => AuthenticatedRoute, } as any) +const AuthenticatedConnectRoute = AuthenticatedConnectRouteImport.update({ + id: '/connect', + path: '/connect', + getParentRoute: () => AuthenticatedRoute, +} as any) const AuthenticatedSourcesRoute = AuthenticatedSourcesRouteImport.update({ id: '/sources', path: '/sources', @@ -146,6 +152,7 @@ export interface FileRoutesByFullPath { '/': typeof AuthenticatedIndexRoute '/admin': typeof AdminRouteWithChildren '/login': typeof LoginRoute + '/connect': typeof AuthenticatedConnectRoute '/sources': typeof AuthenticatedSourcesRoute '/admin/access': typeof AdminAccessRoute '/admin/groups': typeof AdminGroupsRoute @@ -166,6 +173,7 @@ export interface FileRoutesByFullPath { } export interface FileRoutesByTo { '/login': typeof LoginRoute + '/connect': typeof AuthenticatedConnectRoute '/sources': typeof AuthenticatedSourcesRoute '/admin/access': typeof AdminAccessRoute '/admin/groups': typeof AdminGroupsRoute @@ -190,6 +198,7 @@ export interface FileRoutesById { '/_authenticated': typeof AuthenticatedRouteWithChildren '/admin': typeof AdminRouteWithChildren '/login': typeof LoginRoute + '/_authenticated/connect': typeof AuthenticatedConnectRoute '/_authenticated/sources': typeof AuthenticatedSourcesRoute '/admin/access': typeof AdminAccessRoute '/admin/groups': typeof AdminGroupsRoute @@ -215,6 +224,7 @@ export interface FileRouteTypes { | '/' | '/admin' | '/login' + | '/connect' | '/sources' | '/admin/access' | '/admin/groups' @@ -235,6 +245,7 @@ export interface FileRouteTypes { fileRoutesByTo: FileRoutesByTo to: | '/login' + | '/connect' | '/sources' | '/admin/access' | '/admin/groups' @@ -258,6 +269,7 @@ export interface FileRouteTypes { | '/_authenticated' | '/admin' | '/login' + | '/_authenticated/connect' | '/_authenticated/sources' | '/admin/access' | '/admin/groups' @@ -314,6 +326,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AuthenticatedIndexRouteImport parentRoute: typeof AuthenticatedRoute } + '/_authenticated/connect': { + id: '/_authenticated/connect' + path: '/connect' + fullPath: '/connect' + preLoaderRoute: typeof AuthenticatedConnectRouteImport + parentRoute: typeof AuthenticatedRoute + } '/_authenticated/sources': { id: '/_authenticated/sources' path: '/sources' @@ -437,6 +456,7 @@ declare module '@tanstack/react-router' { } interface AuthenticatedRouteChildren { + AuthenticatedConnectRoute: typeof AuthenticatedConnectRoute AuthenticatedSourcesRoute: typeof AuthenticatedSourcesRoute AuthenticatedIndexRoute: typeof AuthenticatedIndexRoute AuthenticatedAssetsIndexRoute: typeof AuthenticatedAssetsIndexRoute @@ -446,6 +466,7 @@ interface AuthenticatedRouteChildren { } const AuthenticatedRouteChildren: AuthenticatedRouteChildren = { + AuthenticatedConnectRoute: AuthenticatedConnectRoute, AuthenticatedSourcesRoute: AuthenticatedSourcesRoute, AuthenticatedIndexRoute: AuthenticatedIndexRoute, AuthenticatedAssetsIndexRoute: AuthenticatedAssetsIndexRoute, diff --git a/web/src/routes/_authenticated/connect.tsx b/web/src/routes/_authenticated/connect.tsx new file mode 100644 index 00000000..10c7dd4d --- /dev/null +++ b/web/src/routes/_authenticated/connect.tsx @@ -0,0 +1,8 @@ +import { createFileRoute } from "@tanstack/react-router" + +import { McpConnectPage } from "@/features/mcp/components/mcp-connect-page" + +export const Route = createFileRoute("/_authenticated/connect")({ + component: McpConnectPage, + staticData: { title: "Connect" }, +}) diff --git a/web/test/e2e/asset-registry-golden-poc.spec.ts b/web/test/e2e/asset-registry-golden-poc.spec.ts new file mode 100644 index 00000000..33551df2 --- /dev/null +++ b/web/test/e2e/asset-registry-golden-poc.spec.ts @@ -0,0 +1,345 @@ +import { expect, test, type Page, type Route } from "@playwright/test" + +const PACK_ID = "a1000000-0000-0000-0000-000000000001" +const PACK_RELEASE_ID = "a1000000-0000-0000-0000-000000000002" +const PACK_REVISION_ID = "a1000000-0000-0000-0000-000000000003" +const REVIEW_ID = "a1000000-0000-0000-0000-000000000004" +const OWNER_ID = "44444444-4444-4444-4444-444444444444" +const REVIEWER_ID = "55555555-5555-5555-5555-555555555555" +const SUPPORT_AGENT_ID = "66666666-6666-6666-6666-666666666666" +const BACKUP_OWNER_ID = "77777777-7777-7777-7777-777777777777" +const ORGANIZATION_ID = "11111111-1111-1111-1111-111111111111" +const DEPARTMENT_ID = "33333333-3333-3333-3333-333333333333" +const INSTRUCTION_ID = "a2000000-0000-0000-0000-000000000001" +const INSTRUCTION_RELEASE_ID = "a2000000-0000-0000-0000-000000000002" +const PROMPT_ID = "a3000000-0000-0000-0000-000000000001" +const PROMPT_RELEASE_ID = "a3000000-0000-0000-0000-000000000002" + +test("two users prove governed release and second-user Pack completion", async ({ + browser, + baseURL, +}) => { + const ownerContext = await browser.newContext({ baseURL }) + const ownerPage = await ownerContext.newPage() + const ownerHarness = await assetHarness(ownerPage, "owner") + + await ownerPage.goto(`/assets/${PACK_ID}/governance`) + await expect(ownerPage.getByRole("heading", { name: "Governance workspace" })).toBeVisible() + await ownerPage.getByRole("tab", { name: "Review" }).click() + await expect(ownerPage.getByText("APPROVED", { exact: true })).toBeVisible() + await expect(ownerPage.getByText("Approved by independent reviewer")).toBeVisible() + await ownerPage.getByRole("tab", { name: "Releases" }).click() + await expect(ownerPage.getByText("1.0.0", { exact: true })).toBeVisible() + await expect(ownerPage.getByText("AVAILABLE", { exact: true })).toBeVisible() + expect(ownerHarness.unexpectedRequests).toEqual([]) + expect(ownerHarness.browserErrors).toEqual([]) + await ownerContext.close() + + const supportContext = await browser.newContext({ baseURL }) + const supportPage = await supportContext.newPage() + const supportHarness = await assetHarness(supportPage, "support") + + await supportPage.goto("/assets") + await expect(supportPage.getByRole("heading", { name: "For your role" })).toBeVisible() + await expect( + supportPage.getByRole("heading", { name: "L1 Customer Support Capability Onboarding" }), + ).toBeVisible() + await expect(supportPage.getByText("Restricted Security Prompt")).toHaveCount(0) + await supportPage.getByRole("link", { name: "Use exact release" }).click() + await expect(supportPage.getByText(`Owner: ${SUPPORT_AGENT_ID}`)).toBeVisible() + await expect(supportPage.getByText(`Backup: ${BACKUP_OWNER_ID}`)).toBeVisible() + await expect(supportPage.getByText("Ownership gap")).toHaveCount(0) + await supportPage.getByRole("link", { name: "Start or resume journey" }).click() + + await expect(supportPage.getByRole("heading", { name: "L1 Customer Support Capability Onboarding" })).toBeVisible() + await expect(supportPage.getByText("0%")).toBeVisible() + await supportPage.getByRole("button", { name: "Mark complete: Classify and respond" }).click() + await expect(supportPage.getByText("50%")).toBeVisible() + await supportPage.getByRole("button", { name: "Mark complete: Triage customer ticket" }).click() + await expect(supportPage.getByText("100%")).toBeVisible() + await expect(supportPage.getByText("COMPLETED", { exact: true })).toBeVisible() + + expect(supportHarness.unexpectedRequests).toEqual([]) + expect(supportHarness.browserErrors).toEqual([]) + expect(supportHarness.requests).toContain("GET /api/assistant/tools/asset-recommendations") + expect( + supportHarness.requests.filter((request) => request.startsWith("PUT /api/assets/")), + ).toHaveLength(2) + await supportContext.close() +}) + +async function assetHarness(page: Page, actor: "owner" | "support") { + const requests: string[] = [] + const unexpectedRequests: string[] = [] + const browserErrors: string[] = [] + const completed = new Set() + + page.on("pageerror", (error) => browserErrors.push(error.message)) + page.on("console", (message) => { + if (message.type() === "error") browserErrors.push(message.text()) + }) + + await page.route("**/api/**", async (route) => { + const request = route.request() + const url = new URL(request.url()) + const signature = `${request.method()} ${url.pathname}` + requests.push(signature) + + if (url.pathname === "/api/session") { + await json(route, session(actor)) + return + } + if (url.pathname === "/api/session/csrf") { + await route.fulfill({ + status: 200, + contentType: "application/json", + headers: { "set-cookie": "XSRF-TOKEN=golden-poc-token; Path=/" }, + body: JSON.stringify({ + headerName: "X-XSRF-TOKEN", + parameterName: "_csrf", + token: "golden-poc-token", + }), + }) + return + } + if ( + actor === "support" && + url.pathname === "/api/assistant/tools/asset-recommendations" + ) { + await json(route, { + traceId: "a4000000-0000-0000-0000-000000000001", + recommendations: [ + { + assetId: PACK_ID, + type: "CAPABILITY_PACK", + namespace: "support", + slug: "l1-onboarding", + title: "L1 Customer Support Capability Onboarding", + summary: "Complete the first correct L1 support ticket", + knowledgeSpaceId: "88888888-8888-4888-8888-888888888802", + portfolioState: "ACTIVE", + releaseId: PACK_RELEASE_ID, + versionLabel: "1.0.0", + releaseDigest: "a".repeat(64), + availability: "AVAILABLE", + }, + ], + }) + return + } + if (url.pathname === `/api/assets/${PACK_ID}`) { + await json(route, packAsset()) + return + } + if ( + actor === "support" && + url.pathname === + `/api/assets/${PACK_ID}/releases/${PACK_RELEASE_ID}/pack-journey` + ) { + await json(route, packJourney(completed)) + return + } + if ( + actor === "support" && + request.method() === "PUT" && + url.pathname.startsWith( + `/api/assets/${PACK_ID}/releases/${PACK_RELEASE_ID}/pack-progress/`, + ) + ) { + completed.add(url.pathname.split("/").at(-1)!) + await json(route, packJourney(completed)) + return + } + + unexpectedRequests.push(signature) + await json(route, { message: "Unexpected golden POC request" }, 500) + }) + + return { requests, unexpectedRequests, browserErrors } +} + +function session(actor: "owner" | "support") { + return { + authenticated: true, + name: actor === "owner" ? "Operations Lead" : "Support Agent", + email: actor === "owner" ? "lead@example.test" : "agent@example.test", + userId: actor === "owner" ? OWNER_ID : SUPPORT_AGENT_ID, + organizationId: ORGANIZATION_ID, + departmentId: DEPARTMENT_ID, + role: actor === "owner" ? "MANAGER" : "EMPLOYEE", + } +} + +function packAsset() { + const payload = JSON.stringify({ + purpose: "ROLE_ONBOARDING", + audience: "L1 support agent", + prerequisites: ["Active support account"], + expectedOutcome: "Complete the first correct L1 support ticket", + items: [ + { key: "instruction", required: true, kind: "REGISTRY_RELEASE" }, + { key: "prompt", required: true, kind: "REGISTRY_RELEASE" }, + ], + completionCriteria: ["Required items complete"], + }) + const timestamp = "2026-07-26T00:00:00Z" + return { + id: PACK_ID, + type: "CAPABILITY_PACK", + namespace: "support", + slug: "l1-onboarding", + knowledgeSpaceId: "88888888-8888-4888-8888-888888888802", + portfolioState: "ACTIVE", + authorizationReady: true, + draft: { + id: "a1000000-0000-0000-0000-000000000005", + lockVersion: 1, + title: "L1 Customer Support Capability Onboarding", + summary: "Complete the first correct L1 support ticket", + classification: "INTERNAL", + schemaVersion: "1", + payload, + editedByUserId: OWNER_ID, + updatedAt: timestamp, + }, + revisions: [ + { + id: PACK_REVISION_ID, + sequence: 1, + title: "L1 Customer Support Capability Onboarding", + summary: "Complete the first correct L1 support ticket", + classification: "INTERNAL", + schemaVersion: "1", + payload, + digest: "a".repeat(64), + changeNote: "Initial L1 onboarding release", + createdByUserId: OWNER_ID, + createdAt: timestamp, + }, + ], + reviews: [ + { + id: REVIEW_ID, + revisionId: PACK_REVISION_ID, + revisionDigest: "a".repeat(64), + state: "APPROVED", + policyVersion: "asset-review-v1", + requestedByUserId: OWNER_ID, + createdAt: timestamp, + resolvedAt: timestamp, + decisions: [ + { + reviewerUserId: REVIEWER_ID, + decision: "APPROVE", + comment: "Approved by independent reviewer", + decidedAt: timestamp, + }, + ], + }, + ], + releases: [ + { + id: PACK_RELEASE_ID, + revisionId: PACK_REVISION_ID, + sequence: 1, + versionLabel: "1.0.0", + title: "L1 Customer Support Capability Onboarding", + summary: "Complete the first correct L1 support ticket", + classification: "INTERNAL", + schemaVersion: "1", + payload, + digest: "a".repeat(64), + releasedByUserId: OWNER_ID, + releasedAt: timestamp, + availability: "AVAILABLE", + availabilityHistory: [ + { + availability: "AVAILABLE", + reason: "Initial release", + changedByUserId: OWNER_ID, + effectiveAt: timestamp, + }, + ], + }, + ], + ownershipHealth: { + ownerPresent: true, + backupOwnerPresent: true, + orphaned: false, + continuityAtRisk: false, + }, + roleAssignments: [ + roleAssignment(SUPPORT_AGENT_ID, "OWNER"), + roleAssignment(BACKUP_OWNER_ID, "BACKUP_OWNER"), + ], + } +} + +function roleAssignment(principalId: string, role: "OWNER" | "BACKUP_OWNER") { + return { + id: role === "OWNER" + ? "a5000000-0000-0000-0000-000000000001" + : "a5000000-0000-0000-0000-000000000002", + principalType: "user", + principalId, + role, + validFrom: "2026-07-26T00:00:00Z", + assignedByUserId: OWNER_ID, + projectedAt: "2026-07-26T00:00:01Z", + } +} + +function packJourney(completed: Set) { + const items = [ + { + key: "instruction", + required: true, + order: 1, + kind: "REGISTRY_RELEASE", + resourceId: INSTRUCTION_ID, + pinnedVersionId: INSTRUCTION_RELEASE_ID, + title: "Classify and respond", + versionLabel: "1.0.0", + availability: "AVAILABLE", + completed: completed.has("instruction"), + }, + { + key: "prompt", + required: true, + order: 2, + kind: "REGISTRY_RELEASE", + resourceId: PROMPT_ID, + pinnedVersionId: PROMPT_RELEASE_ID, + title: "Triage customer ticket", + versionLabel: "1.0.0", + availability: "AVAILABLE", + completed: completed.has("prompt"), + }, + ] + return { + assignmentId: "a6000000-0000-0000-0000-000000000001", + packAssetId: PACK_ID, + packReleaseId: PACK_RELEASE_ID, + releaseDigest: "a".repeat(64), + title: "L1 Customer Support Capability Onboarding", + versionLabel: "1.0.0", + purpose: "ROLE_ONBOARDING", + audience: "L1 support agent", + expectedOutcome: "Complete the first correct L1 support ticket", + status: completed.size === items.length ? "COMPLETED" : "IN_PROGRESS", + accessGap: false, + completedAccessibleItems: completed.size, + items, + startedAt: "2026-07-26T00:00:00Z", + completedAt: + completed.size === items.length ? "2026-07-26T00:05:00Z" : undefined, + } +} + +async function json(route: Route, body: unknown, status = 200) { + await route.fulfill({ + status, + contentType: "application/json", + body: JSON.stringify(body), + }) +} diff --git a/web/test/e2e/mcp-connect.spec.ts b/web/test/e2e/mcp-connect.spec.ts new file mode 100644 index 00000000..e8040562 --- /dev/null +++ b/web/test/e2e/mcp-connect.spec.ts @@ -0,0 +1,54 @@ +import { expect, test } from "@playwright/test" + +test("shows generic MCP onboarding for Claude, Codex, and compatible clients", async ({ + page, +}) => { + const browserErrors: string[] = [] + page.on("pageerror", (error) => browserErrors.push(error.message)) + page.on("console", (message) => { + if (message.type() === "error") browserErrors.push(message.text()) + }) + + await page.route("**/api/session", (route) => + route.fulfill({ + status: 200, + contentType: "application/json", + body: JSON.stringify({ + authenticated: true, + name: "Support Agent", + email: "agent@example.test", + userId: "66666666-6666-4666-8666-666666666666", + organizationId: "11111111-1111-4111-8111-111111111111", + departmentId: "33333333-3333-4333-8333-333333333333", + role: "EMPLOYEE", + }), + }), + ) + + await page.goto("/connect") + + await expect( + page.getByRole("heading", { name: "Connect AI clients" }), + ).toBeVisible() + await expect(page.getByText("https://om.kl3in.tech/mcp", { exact: true })).toBeVisible() + await expect(page.getByText("Read only", { exact: true })).toBeVisible() + + await page.getByRole("tab", { name: "Codex" }).click() + await expect( + page.getByText( + "codex mcp add orgmemory --url https://om.kl3in.tech/mcp --oauth-resource https://om.kl3in.tech/mcp", + { exact: true }, + ), + ).toBeVisible() + await expect( + page.getByText("codex mcp login orgmemory --scopes assets:read", { + exact: true, + }), + ).toBeVisible() + + await page.getByRole("tab", { name: "Other clients" }).click() + await expect(page.getByText("Client ID Metadata Documents", { exact: false })).toBeVisible() + await expect(page.getByText("Dynamic Client Registration", { exact: false })).toBeVisible() + await expect(page.getByText("No mutations in this POC", { exact: true })).toBeVisible() + expect(browserErrors).toEqual([]) +}) From 75de4e6e2851176254612ffe33654b0d6abfe6ac Mon Sep 17 00:00:00 2001 From: kl3inIT Date: Sun, 26 Jul 2026 16:46:41 +0700 Subject: [PATCH 3/5] fix(asset-registry): close review findings --- apps/api/build.gradle.kts | 7 + .../AssetRegistryIntegrationTests.java | 121 +++++++++++++++--- .../capability-pack-template.json | 11 +- .../fixtures/asset-registry/mock-tickets.json | 16 +-- .../asset-registry/prompt-template.json | 17 +-- .../asset-registry/quality-checklist.json | 2 +- .../asset-registry/success-metrics.json | 12 +- .../scripts/configure-keycloak-mcp.sh | 82 ++++++++---- .../scripts/test-keycloak-mcp-onboarding.sh | 48 +++++-- .../keycloak/mcp-client-profiles.json | 2 - .../e2e/asset-registry-golden-poc.spec.ts | 29 ++++- 11 files changed, 264 insertions(+), 83 deletions(-) diff --git a/apps/api/build.gradle.kts b/apps/api/build.gradle.kts index a2331359..0250da18 100644 --- a/apps/api/build.gradle.kts +++ b/apps/api/build.gradle.kts @@ -1,4 +1,5 @@ import org.gradle.api.tasks.testing.Test +import org.gradle.language.jvm.tasks.ProcessResources plugins { id("orgmemory.spring-boot-app-conventions") @@ -42,3 +43,9 @@ tasks.withType().configureEach { systemProperty("spring.session.jdbc.cleanup-cron", "-") systemProperty("orgmemory.graph-rag.postgres.apache-age-mode", "DISABLED") } + +tasks.named("processTestResources") { + from(rootProject.file("demo/fixtures/asset-registry")) { + into("golden/asset-registry") + } +} diff --git a/apps/api/src/test/java/com/orgmemory/api/assetregistry/AssetRegistryIntegrationTests.java b/apps/api/src/test/java/com/orgmemory/api/assetregistry/AssetRegistryIntegrationTests.java index d0a2f219..6e02b47a 100644 --- a/apps/api/src/test/java/com/orgmemory/api/assetregistry/AssetRegistryIntegrationTests.java +++ b/apps/api/src/test/java/com/orgmemory/api/assetregistry/AssetRegistryIntegrationTests.java @@ -45,16 +45,18 @@ import com.orgmemory.core.authorization.RelationshipTupleWritePort; import com.orgmemory.core.authorization.RelationshipTupleWriteResult; import com.orgmemory.core.authorization.ResourceRef; +import com.orgmemory.core.knowledge.KnowledgeCatalogItem; +import com.orgmemory.core.knowledge.KnowledgeCatalogService; import com.orgmemory.core.knowledge.QueryEmbeddingPort; import com.orgmemory.core.knowledge.PermissionAwareKnowledgeSearch; import com.orgmemory.core.knowledge.RetrievedKnowledgeEvidence; import com.orgmemory.core.knowledge.SecureKnowledgeSearchResult; import com.orgmemory.core.organization.CurrentActor; +import com.orgmemory.core.permission.KnowledgeClassification; import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.UUID; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -96,6 +98,10 @@ class AssetRegistryIntegrationTests { UUID.fromString("77777777-7777-7777-7777-777777777777"); private static final UUID SPACE_ID = UUID.fromString("88888888-8888-4888-8888-888888888802"); + private static final UUID GOLDEN_KNOWLEDGE_ASSET_ID = + UUID.fromString("90000000-0000-0000-0000-000000000002"); + private static final UUID GOLDEN_KNOWLEDGE_VERSION_ID = + UUID.fromString("90000000-0000-0000-0000-000000000007"); private static final String MODEL_ID = "asset-model-1"; private static final AiRoute PROMPT_ROUTE = new AiRoute("test-gateway", "test-model"); @@ -163,6 +169,9 @@ class AssetRegistryIntegrationTests { @MockitoBean PermissionAwareKnowledgeSearch knowledgeSearch; + @MockitoBean + KnowledgeCatalogService knowledgeCatalog; + @MockitoBean ChatModelPort chat; @@ -182,6 +191,8 @@ void prepare() { when(knowledgeSearch.search(any(), any(), any(), any())) .thenReturn(new SecureKnowledgeSearchResult( "asset-registry-empty-grounding", List.of())); + when(knowledgeCatalog.findExactVisible(any(), any(), any())) + .thenReturn(Optional.empty()); } @TestConfiguration(proxyBeanMethods = false) @@ -860,7 +871,19 @@ void goldenPocTransfersAReleasedSupportCapabilityToASecondUser() new TypeReference<>() { }); assertEquals(8, tickets.size()); - assertTrue(tickets.stream().allMatch(MockTicket::rubricPass)); + when(knowledgeCatalog.findExactVisible( + any(), + eq(GOLDEN_KNOWLEDGE_ASSET_ID), + eq(GOLDEN_KNOWLEDGE_VERSION_ID))) + .thenReturn(Optional.of(new KnowledgeCatalogItem( + GOLDEN_KNOWLEDGE_ASSET_ID, + GOLDEN_KNOWLEDGE_VERSION_ID, + 1, + SPACE_ID, + "Support SLA and escalation", + "en", + KnowledgeClassification.INTERNAL, + "b".repeat(64)))); when(knowledgeSearch.search(any(), any(), any(), any())) .thenAnswer(invocation -> new SecureKnowledgeSearchResult( @@ -883,6 +906,7 @@ void goldenPocTransfersAReleasedSupportCapabilityToASecondUser() "category", ticket.category(), "slaTier", ticket.slaTier(), "escalate", ticket.escalate(), + "accountableTeam", ticket.accountableTeam(), "response", "Use approved policy and cite support.sla-and-escalation@1"))); }); @@ -903,7 +927,13 @@ void goldenPocTransfersAReleasedSupportCapabilityToASecondUser() .replace("${WORK_INSTRUCTION_ASSET_ID}", instruction.id().toString()) .replace("${WORK_INSTRUCTION_RELEASE_ID}", instructionRelease.id().toString()) .replace("${PROMPT_ASSET_ID}", prompt.id().toString()) - .replace("${PROMPT_RELEASE_ID}", promptRelease.id().toString()); + .replace("${PROMPT_RELEASE_ID}", promptRelease.id().toString()) + .replace( + "${KNOWLEDGE_ASSET_ID}", + GOLDEN_KNOWLEDGE_ASSET_ID.toString()) + .replace( + "${KNOWLEDGE_VERSION_ID}", + GOLDEN_KNOWLEDGE_VERSION_ID.toString()); AssetView pack = createApprovedRelease( AssetType.CAPABILITY_PACK, "l1-onboarding", @@ -983,10 +1013,47 @@ void goldenPocTransfersAReleasedSupportCapabilityToASecondUser() "support SLA escalation", "golden-poc-first-correct-task"); assertTrue(firstCorrectTask.output().contains("\"category\":\"billing\"")); + assertTrue(firstCorrectTask.output().contains("\"accountableTeam\":\"NONE\"")); assertEquals(1, firstCorrectTask.citations().size()); + PromptRunResult.PromptCitation citation = + firstCorrectTask.citations().getFirst(); assertEquals( - "SLA and escalation", - firstCorrectTask.citations().getFirst().title()); + UUID.fromString("90000000-0000-0000-0000-000000000001"), + citation.chunkId()); + assertEquals(GOLDEN_KNOWLEDGE_ASSET_ID, citation.knowledgeAssetId()); + assertEquals( + UUID.fromString("90000000-0000-0000-0000-000000000004"), + citation.sourceRevisionId()); + assertEquals("SLA and escalation", citation.title()); + assertEquals("Response tiers", citation.heading()); + assertTrue(ticketPassesRubric(tickets.getFirst(), firstCorrectTask)); + assertFalse(ticketPassesRubric( + tickets.getFirst(), + new PromptRunResult( + UUID.randomUUID(), + prompt.id(), + promptRelease.id(), + promptRelease.digest(), + PROMPT_ROUTE, + """ + {"category":"billing","slaTier":"P0","escalate":true,\ + "accountableTeam":"INCIDENT_RESPONSE","response":"unsupported"} + """, + List.of(), + 1))); + + Map storedGoldenRun = jdbc.queryForMap( + """ + select citation_refs::text as citations, + sanitized_outcome::text as outcome + from prompt_runs + where id = ? + """, + firstCorrectTask.runId()); + assertTrue(storedGoldenRun.get("citations").toString() + .contains(GOLDEN_KNOWLEDGE_ASSET_ID.toString())); + assertFalse(storedGoldenRun.toString().contains(tickets.getFirst().text())); + assertFalse(storedGoldenRun.toString().contains(firstCorrectTask.output())); WorkInstructionView acknowledged = instructions.acknowledge( SUPPORT_AGENT, instruction.id(), instructionRelease.id()); @@ -1002,7 +1069,7 @@ void goldenPocTransfersAReleasedSupportCapabilityToASecondUser() true); } assertEquals(PackAssignmentStatus.COMPLETED, journey.status()); - assertEquals(2, journey.completedAccessibleItems()); + assertEquals(3, journey.completedAccessibleItems()); AssetView changedPrompt = assets.updateDraft( AUTHOR, @@ -1086,8 +1153,6 @@ select count(*) """, Integer.class, prompt.id())); - assertTrue(goldenFixture("success-metrics.json") - .contains("\"evaluation_pass\"")); } private AssetView create(String slug) { @@ -1330,16 +1395,32 @@ private static RetrievedKnowledgeEvidence goldenKnowledgeEvidence() { } private static String goldenFixture(String name) throws IOException { - Path current = Path.of("").toAbsolutePath(); - while (current != null - && !Files.exists(current.resolve("settings.gradle.kts"))) { - current = current.getParent(); - } - if (current == null) { - throw new IllegalStateException("Could not locate the repository root"); + String resource = "/golden/asset-registry/" + name; + try (var stream = + AssetRegistryIntegrationTests.class.getResourceAsStream(resource)) { + if (stream == null) { + throw new IOException("Missing golden fixture: " + resource); + } + return new String(stream.readAllBytes(), java.nio.charset.StandardCharsets.UTF_8); } - return Files.readString(current.resolve( - "demo/fixtures/asset-registry/" + name)); + } + + private static boolean ticketPassesRubric( + MockTicket ticket, PromptRunResult result) throws IOException { + Map output = JSON.readValue( + result.output(), + new TypeReference<>() { + }); + return ticket.category().equals(output.get("category")) + && ticket.slaTier().equals(output.get("slaTier")) + && Boolean.valueOf(ticket.escalate()).equals(output.get("escalate")) + && ticket.accountableTeam().equals(output.get("accountableTeam")) + && output.get("response").toString() + .contains("support.sla-and-escalation@1") + && ticket.allowedCitations() + .contains("support.sla-and-escalation@1") + && result.citations().stream().anyMatch(citation -> + GOLDEN_KNOWLEDGE_ASSET_ID.equals(citation.knowledgeAssetId())); } private record MockTicket( @@ -1349,8 +1430,8 @@ private record MockTicket( String category, String slaTier, boolean escalate, - List allowedCitations, - boolean rubricPass) { + String accountableTeam, + List allowedCitations) { } private void clearAssetRegistry() { diff --git a/demo/fixtures/asset-registry/capability-pack-template.json b/demo/fixtures/asset-registry/capability-pack-template.json index ac35b4e6..eec5c244 100644 --- a/demo/fixtures/asset-registry/capability-pack-template.json +++ b/demo/fixtures/asset-registry/capability-pack-template.json @@ -7,6 +7,15 @@ ], "expectedOutcome": "The agent can classify, ground, draft, verify, and record a correct first ticket", "items": [ + { + "key": "knowledge", + "required": true, + "kind": "KNOWLEDGE_VERSION", + "assetId": null, + "releaseId": null, + "knowledgeAssetId": "${KNOWLEDGE_ASSET_ID}", + "knowledgeVersionId": "${KNOWLEDGE_VERSION_ID}" + }, { "key": "instruction", "required": true, @@ -27,7 +36,7 @@ } ], "completionCriteria": [ - "Both required released items are complete", + "All three required exact-pinned items are complete", "One mock ticket passes support.triage-quality@1", "The execution trace pins the Prompt release and grounding citation" ], diff --git a/demo/fixtures/asset-registry/mock-tickets.json b/demo/fixtures/asset-registry/mock-tickets.json index 35f6c954..2a668d7a 100644 --- a/demo/fixtures/asset-registry/mock-tickets.json +++ b/demo/fixtures/asset-registry/mock-tickets.json @@ -1,10 +1,10 @@ [ - {"id":"TICKET-001","scenario":"billing question","text":"Why is my monthly invoice higher than last month?","category":"billing","slaTier":"P2","escalate":false,"allowedCitations":["support.sla-and-escalation@1"],"rubricPass":true}, - {"id":"TICKET-002","scenario":"password reset","text":"I cannot sign in and need a password reset.","category":"access","slaTier":"P2","escalate":false,"allowedCitations":["support.sla-and-escalation@1"],"rubricPass":true}, - {"id":"TICKET-003","scenario":"degraded service","text":"Reports take ten minutes to load for every user.","category":"degraded_service","slaTier":"P1","escalate":true,"allowedCitations":["support.sla-and-escalation@1"],"rubricPass":true}, - {"id":"TICKET-004","scenario":"confirmed outage","text":"The production API is unavailable in all regions.","category":"outage","slaTier":"P0","escalate":true,"allowedCitations":["support.sla-and-escalation@1"],"rubricPass":true}, - {"id":"TICKET-005","scenario":"suspected security issue","text":"I saw an unknown login and possible account takeover.","category":"security","slaTier":"P0","escalate":true,"allowedCitations":["support.sla-and-escalation@1"],"rubricPass":true}, - {"id":"TICKET-006","scenario":"data deletion request","text":"Please permanently delete my account data.","category":"privacy","slaTier":"P1","escalate":true,"allowedCitations":["support.sla-and-escalation@1"],"rubricPass":true}, - {"id":"TICKET-007","scenario":"duplicate ticket","text":"This repeats my open ticket number 4312.","category":"duplicate","slaTier":"P2","escalate":false,"allowedCitations":["support.sla-and-escalation@1"],"rubricPass":true}, - {"id":"TICKET-008","scenario":"abusive message","text":"Your service is useless and your staff are idiots.","category":"abuse","slaTier":"P2","escalate":true,"allowedCitations":["support.sla-and-escalation@1"],"rubricPass":true} + {"id":"TICKET-001","scenario":"billing question","text":"Why is my monthly invoice higher than last month?","category":"billing","slaTier":"P2","escalate":false,"accountableTeam":"NONE","allowedCitations":["support.sla-and-escalation@1"]}, + {"id":"TICKET-002","scenario":"password reset","text":"I cannot sign in and need a password reset.","category":"access","slaTier":"P2","escalate":false,"accountableTeam":"NONE","allowedCitations":["support.sla-and-escalation@1"]}, + {"id":"TICKET-003","scenario":"degraded service","text":"Reports take ten minutes to load for every user.","category":"degraded_service","slaTier":"P1","escalate":true,"accountableTeam":"INCIDENT_RESPONSE","allowedCitations":["support.sla-and-escalation@1"]}, + {"id":"TICKET-004","scenario":"confirmed outage","text":"The production API is unavailable in all regions.","category":"outage","slaTier":"P0","escalate":true,"accountableTeam":"INCIDENT_RESPONSE","allowedCitations":["support.sla-and-escalation@1"]}, + {"id":"TICKET-005","scenario":"suspected security issue","text":"I saw an unknown login and possible account takeover.","category":"security","slaTier":"P0","escalate":true,"accountableTeam":"SECURITY","allowedCitations":["support.sla-and-escalation@1"]}, + {"id":"TICKET-006","scenario":"data deletion request","text":"Please permanently delete my account data.","category":"privacy","slaTier":"P1","escalate":true,"accountableTeam":"PRIVACY","allowedCitations":["support.sla-and-escalation@1"]}, + {"id":"TICKET-007","scenario":"duplicate ticket","text":"This repeats my open ticket number 4312.","category":"duplicate","slaTier":"P2","escalate":false,"accountableTeam":"NONE","allowedCitations":["support.sla-and-escalation@1"]}, + {"id":"TICKET-008","scenario":"abusive message","text":"Your service is useless and your staff are idiots.","category":"abuse","slaTier":"P2","escalate":true,"accountableTeam":"TRUST_AND_SAFETY","allowedCitations":["support.sla-and-escalation@1"]} ] diff --git a/demo/fixtures/asset-registry/prompt-template.json b/demo/fixtures/asset-registry/prompt-template.json index 21cafdc4..62a067c8 100644 --- a/demo/fixtures/asset-registry/prompt-template.json +++ b/demo/fixtures/asset-registry/prompt-template.json @@ -27,6 +27,7 @@ "category", "slaTier", "escalate", + "accountableTeam", "response" ] }, @@ -46,7 +47,7 @@ "variables": { "ticket_text": "TICKET-001: Why is my monthly invoice higher than last month?" }, - "expectedContains": ["billing", "P2", "false"], + "expectedContains": ["billing", "P2", "false", "NONE"], "forbiddenContains": ["password", "secret"] }, { @@ -54,7 +55,7 @@ "variables": { "ticket_text": "TICKET-002: I cannot sign in and need a password reset." }, - "expectedContains": ["access", "P2", "false"], + "expectedContains": ["access", "P2", "false", "NONE"], "forbiddenContains": ["send your password", "secret"] }, { @@ -62,7 +63,7 @@ "variables": { "ticket_text": "TICKET-003: Reports take ten minutes to load for every user." }, - "expectedContains": ["degraded_service", "P1", "true"], + "expectedContains": ["degraded_service", "P1", "true", "INCIDENT_RESPONSE"], "forbiddenContains": ["guarantee"] }, { @@ -70,7 +71,7 @@ "variables": { "ticket_text": "TICKET-004: The production API is unavailable in all regions." }, - "expectedContains": ["outage", "P0", "true"], + "expectedContains": ["outage", "P0", "true", "INCIDENT_RESPONSE"], "forbiddenContains": ["root cause is"] }, { @@ -78,7 +79,7 @@ "variables": { "ticket_text": "TICKET-005: I saw an unknown login and possible account takeover." }, - "expectedContains": ["security", "P0", "true"], + "expectedContains": ["security", "P0", "true", "SECURITY"], "forbiddenContains": ["share your token"] }, { @@ -86,7 +87,7 @@ "variables": { "ticket_text": "TICKET-006: Please permanently delete my account data." }, - "expectedContains": ["privacy", "P1", "true"], + "expectedContains": ["privacy", "P1", "true", "PRIVACY"], "forbiddenContains": ["already deleted"] }, { @@ -94,7 +95,7 @@ "variables": { "ticket_text": "TICKET-007: This repeats my open ticket number 4312." }, - "expectedContains": ["duplicate", "P2", "false"], + "expectedContains": ["duplicate", "P2", "false", "NONE"], "forbiddenContains": ["closed"] }, { @@ -102,7 +103,7 @@ "variables": { "ticket_text": "TICKET-008: Your service is useless and your staff are idiots." }, - "expectedContains": ["abuse", "P2", "true"], + "expectedContains": ["abuse", "P2", "true", "TRUST_AND_SAFETY"], "forbiddenContains": ["insult"] } ], diff --git a/demo/fixtures/asset-registry/quality-checklist.json b/demo/fixtures/asset-registry/quality-checklist.json index 0251fabd..a887712c 100644 --- a/demo/fixtures/asset-registry/quality-checklist.json +++ b/demo/fixtures/asset-registry/quality-checklist.json @@ -6,7 +6,7 @@ {"key": "escalation", "required": true, "description": "Escalation decision and accountable team are correct"}, {"key": "grounding", "required": true, "description": "SLA or escalation claims cite support.sla-and-escalation@1"}, {"key": "tone", "required": true, "description": "Response is calm, factual, and non-retaliatory"}, - {"key": "schema", "required": true, "description": "Output contains category, slaTier, escalate, and response"}, + {"key": "schema", "required": true, "description": "Output contains category, slaTier, escalate, accountableTeam, and response"}, {"key": "safety", "required": true, "description": "No secret, unsupported promise, or sensitive raw value is retained"} ] } diff --git a/demo/fixtures/asset-registry/success-metrics.json b/demo/fixtures/asset-registry/success-metrics.json index 0d5205be..98815345 100644 --- a/demo/fixtures/asset-registry/success-metrics.json +++ b/demo/fixtures/asset-registry/success-metrics.json @@ -1,13 +1,13 @@ { "definitions": [ {"key":"time_to_first_correct_task","formula":"correct_task_completed_at - pack_first_viewed_at","pocThreshold":"captured; no benchmark claim"}, - {"key":"first_time_right","formula":"tasks_passing_without_reviewer_correction / attempted_tasks","pocThreshold":"1.0 for the deterministic golden ticket"}, + {"key":"first_time_right","formula":"tasks_passing_without_reviewer_correction / attempted_tasks","emptyCohortValue":null,"pocThreshold":"1.0 for the deterministic golden ticket"}, {"key":"second_user_reuse","formula":"distinct_non_author_users_with_successful_use","pocThreshold":">= 1"}, - {"key":"view_to_use","formula":"distinct_users_with_use / distinct_users_with_view","pocThreshold":"1.0 in the scripted golden flow"}, - {"key":"evaluation_pass","formula":"passed_evaluation_cases / total_evaluation_cases","pocThreshold":"8 / 8"}, - {"key":"reviewer_correction","formula":"revisions_with_changes_requested / submitted_revisions","pocThreshold":"captured; no benchmark claim"}, - {"key":"owner_coverage","formula":"assets_with_active_owner_and_backup / active_assets","pocThreshold":"1.0 after handover"}, + {"key":"view_to_use","formula":"distinct_users_with_use / distinct_users_with_view","emptyCohortValue":null,"pocThreshold":"1.0 in the scripted golden flow"}, + {"key":"evaluation_pass","formula":"passed_evaluation_cases / total_evaluation_cases","emptyCohortValue":null,"pocThreshold":"8 / 8"}, + {"key":"reviewer_correction","formula":"revisions_with_changes_requested / submitted_revisions","emptyCohortValue":null,"pocThreshold":"captured; no benchmark claim"}, + {"key":"owner_coverage","formula":"assets_with_active_owner_and_backup / active_assets","emptyCohortValue":null,"pocThreshold":"1.0 after handover"}, {"key":"unauthorized_metadata_leakage","formula":"denied_responses_containing_private_asset_metadata","pocThreshold":"0"} ], - "measurementPolicy": "POC values are technical evidence from deterministic fixtures, not customer adoption benchmarks." + "measurementPolicy": "POC values are technical evidence from deterministic fixtures, not customer adoption benchmarks. Ratio metrics return null for an empty cohort; null means not applicable and is never coerced to zero." } diff --git a/infrastructure/deployment/scripts/configure-keycloak-mcp.sh b/infrastructure/deployment/scripts/configure-keycloak-mcp.sh index 546497bf..5590123a 100755 --- a/infrastructure/deployment/scripts/configure-keycloak-mcp.sh +++ b/infrastructure/deployment/scripts/configure-keycloak-mcp.sh @@ -65,9 +65,41 @@ kcadm get client-scopes \ --format csv \ --noquotes \ | tr -d '\r' >"$client_scopes_csv" -if ! awk -F, '$2 == "basic" { found = 1 } END { exit !found }' \ - "$client_scopes_csv"; then +basic_scope_id="$( + awk -F, '$2 == "basic" { print $1; exit }' "$client_scopes_csv" +)" +if [[ -z "$basic_scope_id" ]]; then kcadm create client-scopes -r "$realm" -f - <"$basic_scope_source" >/dev/null +else + basic_scope_current="$tmp_dir/basic-scope-current.json" + basic_scope_synced="$tmp_dir/basic-scope-synced.json" + kcadm get "client-scopes/$basic_scope_id" \ + -r "$realm" >"$basic_scope_current" + python3 \ + - "$basic_scope_current" "$basic_scope_source" >"$basic_scope_synced" <<'PY' +import json +import sys + +current_path, desired_path = sys.argv[1:] +with open(current_path, encoding="utf-8") as stream: + current = json.load(stream) +with open(desired_path, encoding="utf-8") as stream: + desired = json.load(stream) + +desired["id"] = current["id"] +current_mapper_ids = { + mapper.get("name"): mapper.get("id") + for mapper in current.get("protocolMappers", []) +} +for mapper in desired.get("protocolMappers", []): + mapper_id = current_mapper_ids.get(mapper.get("name")) + if mapper_id: + mapper["id"] = mapper_id +json.dump(desired, sys.stdout) +PY + kcadm update "client-scopes/$basic_scope_id" \ + -r "$realm" \ + -f - <"$basic_scope_synced" fi merge_client_policy_document() { @@ -135,27 +167,31 @@ for provider_id in "${registration_providers[@]}"; do exit 1 fi - case "$provider_id" in - trusted-hosts) - kcadm update "components/$component_id" -r "$realm" \ - -s 'config."host-sending-registration-request-must-match"=["false"]' \ - -s 'config."trusted-hosts"=["localhost","127.0.0.1","claude.ai","claude.com","vscode.dev"]' \ - -s 'config."client-uris-must-match"=["true"]' - ;; - allowed-client-templates) - kcadm update "components/$component_id" -r "$realm" \ - -s 'config."allow-default-scopes"=["true"]' \ - -s 'config."allowed-client-scopes"=["basic","assets:read"]' - ;; - max-clients) - kcadm update "components/$component_id" -r "$realm" \ - -s 'config."max-clients"=["50"]' - ;; - *) - printf 'Unsupported MCP registration policy: %s\n' "$provider_id" >&2 - exit 1 - ;; - esac + mapfile -t policy_settings < <( + python3 - "$registration_policy_source" "$provider_id" <<'PY' +import json +import sys + +policy_path, provider_id = sys.argv[1:] +with open(policy_path, encoding="utf-8") as stream: + policies = json.load(stream) +if provider_id not in policies: + raise SystemExit(f"Unsupported MCP registration policy: {provider_id}") +for key, values in policies[provider_id].items(): + print(f'config."{key}"={json.dumps(values, separators=(",", ":"))}') +PY + ) + if [[ "${#policy_settings[@]}" -eq 0 ]]; then + printf 'MCP registration policy has no settings: %s\n' "$provider_id" >&2 + exit 1 + fi + policy_set_args=() + for setting in "${policy_settings[@]}"; do + policy_set_args+=(-s "$setting") + done + kcadm update "components/$component_id" \ + -r "$realm" \ + "${policy_set_args[@]}" component_path="$tmp_dir/component-${provider_id}.json" kcadm get "components/$component_id" -r "$realm" >"$component_path" diff --git a/infrastructure/deployment/scripts/test-keycloak-mcp-onboarding.sh b/infrastructure/deployment/scripts/test-keycloak-mcp-onboarding.sh index cffb2089..e85c0218 100755 --- a/infrastructure/deployment/scripts/test-keycloak-mcp-onboarding.sh +++ b/infrastructure/deployment/scripts/test-keycloak-mcp-onboarding.sh @@ -7,6 +7,7 @@ container="orgmemory-keycloak-mcp-test-${run_id}" image="orgmemory-keycloak-mcp-test:${run_id}" tmp_root="${TMPDIR:-/tmp}" tmp_dir="$(mktemp -d "${tmp_root%/}/orgmemory-keycloak-mcp-test.XXXXXX")" +container_kcadm_config="/tmp/orgmemory-mcp-test-${run_id}.config" cleanup() { if [[ "$(docker inspect "$container" --format '{{.Name}}' 2>/dev/null || true)" == "/$container" ]]; then @@ -68,9 +69,47 @@ fi ORGMEMORY_KEYCLOAK_CONTAINER="$container" \ ORGMEMORY_KEYCLOAK_REALM=orgmemory \ "$repo_root/infrastructure/deployment/scripts/configure-keycloak-mcp.sh" +MSYS_NO_PATHCONV=1 docker exec "$container" sh -ec \ + '/opt/keycloak/bin/kcadm.sh config credentials \ + --config "$1" \ + --server http://127.0.0.1:8080 \ + --realm master \ + --user "$KC_BOOTSTRAP_ADMIN_USERNAME" \ + --password "$KC_BOOTSTRAP_ADMIN_PASSWORD" >/dev/null' \ + sh "$container_kcadm_config" +MSYS_NO_PATHCONV=1 docker exec "$container" \ + /opt/keycloak/bin/kcadm.sh get client-scopes \ + -r orgmemory \ + -q name=basic \ + --config "$container_kcadm_config" >"$tmp_dir/basic-scope.json" +basic_scope_id="$( + python3 -c \ + 'import json,sys; print(json.load(open(sys.argv[1], encoding="utf-8"))[0]["id"])' \ + "$tmp_dir/basic-scope.json" +)" +MSYS_NO_PATHCONV=1 docker exec "$container" \ + /opt/keycloak/bin/kcadm.sh update "client-scopes/$basic_scope_id" \ + -r orgmemory \ + -s description=drifted \ + --config "$container_kcadm_config" >/dev/null ORGMEMORY_KEYCLOAK_CONTAINER="$container" \ ORGMEMORY_KEYCLOAK_REALM=orgmemory \ "$repo_root/infrastructure/deployment/scripts/configure-keycloak-mcp.sh" +MSYS_NO_PATHCONV=1 docker exec "$container" \ + /opt/keycloak/bin/kcadm.sh get "client-scopes/$basic_scope_id" \ + -r orgmemory \ + --config "$container_kcadm_config" >"$tmp_dir/basic-scope.json" +python3 - "$tmp_dir/basic-scope.json" <<'PY' +import json +import sys + +with open(sys.argv[1], encoding="utf-8") as stream: + scope = json.load(stream) +expected_description = ( + "OpenID Connect scope for basic subject and authentication-time claims" +) +assert scope["description"] == expected_description, scope +PY curl --fail --silent --show-error "$metadata_url" >"$tmp_dir/metadata.json" python3 - "$tmp_dir/metadata.json" <<'PY' @@ -107,18 +146,11 @@ client_id="$( 'import json,sys; print(json.load(open(sys.argv[1], encoding="utf-8"))["client_id"])' \ "$tmp_dir/registration.json" )" -MSYS_NO_PATHCONV=1 docker exec "$container" sh -ec \ - '/opt/keycloak/bin/kcadm.sh config credentials \ - --config /tmp/orgmemory-mcp-test.config \ - --server http://127.0.0.1:8080 \ - --realm master \ - --user "$KC_BOOTSTRAP_ADMIN_USERNAME" \ - --password "$KC_BOOTSTRAP_ADMIN_PASSWORD" >/dev/null' MSYS_NO_PATHCONV=1 docker exec "$container" \ /opt/keycloak/bin/kcadm.sh get clients \ -r orgmemory \ -q "clientId=$client_id" \ - --config /tmp/orgmemory-mcp-test.config >"$tmp_dir/client.json" + --config "$container_kcadm_config" >"$tmp_dir/client.json" python3 - "$tmp_dir/client.json" <<'PY' import json import sys diff --git a/infrastructure/keycloak/mcp-client-profiles.json b/infrastructure/keycloak/mcp-client-profiles.json index a84e9d50..c2810cb2 100644 --- a/infrastructure/keycloak/mcp-client-profiles.json +++ b/infrastructure/keycloak/mcp-client-profiles.json @@ -11,8 +11,6 @@ "only-allow-confidential-client": false, "cimd-allow-permitted-domains": [ "claude.ai", - "localhost", - "127.0.0.1", "vscode.dev", "code.visualstudio.com" ], diff --git a/web/test/e2e/asset-registry-golden-poc.spec.ts b/web/test/e2e/asset-registry-golden-poc.spec.ts index 33551df2..a5956017 100644 --- a/web/test/e2e/asset-registry-golden-poc.spec.ts +++ b/web/test/e2e/asset-registry-golden-poc.spec.ts @@ -14,6 +14,8 @@ const INSTRUCTION_ID = "a2000000-0000-0000-0000-000000000001" const INSTRUCTION_RELEASE_ID = "a2000000-0000-0000-0000-000000000002" const PROMPT_ID = "a3000000-0000-0000-0000-000000000001" const PROMPT_RELEASE_ID = "a3000000-0000-0000-0000-000000000002" +const KNOWLEDGE_ID = "90000000-0000-0000-0000-000000000002" +const KNOWLEDGE_VERSION_ID = "90000000-0000-0000-0000-000000000007" test("two users prove governed release and second-user Pack completion", async ({ browser, @@ -52,11 +54,13 @@ test("two users prove governed release and second-user Pack completion", async ( await supportPage.getByRole("link", { name: "Start or resume journey" }).click() await expect(supportPage.getByRole("heading", { name: "L1 Customer Support Capability Onboarding" })).toBeVisible() - await expect(supportPage.getByText("0%")).toBeVisible() + await expect(supportPage.getByText("0%", { exact: true })).toBeVisible() + await supportPage.getByRole("button", { name: "Mark complete: Support SLA and escalation" }).click() + await expect(supportPage.getByText("33%", { exact: true })).toBeVisible() await supportPage.getByRole("button", { name: "Mark complete: Classify and respond" }).click() - await expect(supportPage.getByText("50%")).toBeVisible() + await expect(supportPage.getByText("67%", { exact: true })).toBeVisible() await supportPage.getByRole("button", { name: "Mark complete: Triage customer ticket" }).click() - await expect(supportPage.getByText("100%")).toBeVisible() + await expect(supportPage.getByText("100%", { exact: true })).toBeVisible() await expect(supportPage.getByText("COMPLETED", { exact: true })).toBeVisible() expect(supportHarness.unexpectedRequests).toEqual([]) @@ -64,7 +68,7 @@ test("two users prove governed release and second-user Pack completion", async ( expect(supportHarness.requests).toContain("GET /api/assistant/tools/asset-recommendations") expect( supportHarness.requests.filter((request) => request.startsWith("PUT /api/assets/")), - ).toHaveLength(2) + ).toHaveLength(3) await supportContext.close() }) @@ -177,6 +181,7 @@ function packAsset() { prerequisites: ["Active support account"], expectedOutcome: "Complete the first correct L1 support ticket", items: [ + { key: "knowledge", required: true, kind: "KNOWLEDGE_VERSION" }, { key: "instruction", required: true, kind: "REGISTRY_RELEASE" }, { key: "prompt", required: true, kind: "REGISTRY_RELEASE" }, ], @@ -292,9 +297,21 @@ function roleAssignment(principalId: string, role: "OWNER" | "BACKUP_OWNER") { function packJourney(completed: Set) { const items = [ { - key: "instruction", + key: "knowledge", required: true, order: 1, + kind: "KNOWLEDGE", + resourceId: KNOWLEDGE_ID, + pinnedVersionId: KNOWLEDGE_VERSION_ID, + title: "Support SLA and escalation", + versionLabel: "1", + availability: "AVAILABLE", + completed: completed.has("knowledge"), + }, + { + key: "instruction", + required: true, + order: 2, kind: "REGISTRY_RELEASE", resourceId: INSTRUCTION_ID, pinnedVersionId: INSTRUCTION_RELEASE_ID, @@ -306,7 +323,7 @@ function packJourney(completed: Set) { { key: "prompt", required: true, - order: 2, + order: 3, kind: "REGISTRY_RELEASE", resourceId: PROMPT_ID, pinnedVersionId: PROMPT_RELEASE_ID, From b53141adac6b3276f9240fe5e94943dc5d3a9803 Mon Sep 17 00:00:00 2001 From: kl3inIT Date: Sun, 26 Jul 2026 16:51:40 +0700 Subject: [PATCH 4/5] test(mcp): select the exact Keycloak scope --- .../scripts/test-keycloak-mcp-onboarding.sh | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/infrastructure/deployment/scripts/test-keycloak-mcp-onboarding.sh b/infrastructure/deployment/scripts/test-keycloak-mcp-onboarding.sh index e85c0218..f884ca9a 100755 --- a/infrastructure/deployment/scripts/test-keycloak-mcp-onboarding.sh +++ b/infrastructure/deployment/scripts/test-keycloak-mcp-onboarding.sh @@ -83,9 +83,14 @@ MSYS_NO_PATHCONV=1 docker exec "$container" \ -q name=basic \ --config "$container_kcadm_config" >"$tmp_dir/basic-scope.json" basic_scope_id="$( - python3 -c \ - 'import json,sys; print(json.load(open(sys.argv[1], encoding="utf-8"))[0]["id"])' \ - "$tmp_dir/basic-scope.json" + python3 - "$tmp_dir/basic-scope.json" <<'PY' +import json +import sys + +with open(sys.argv[1], encoding="utf-8") as stream: + scopes = json.load(stream) +print(next(scope["id"] for scope in scopes if scope["name"] == "basic")) +PY )" MSYS_NO_PATHCONV=1 docker exec "$container" \ /opt/keycloak/bin/kcadm.sh update "client-scopes/$basic_scope_id" \ From b0ae5e371d3be1cb357716180a859481d7d80baf Mon Sep 17 00:00:00 2001 From: kl3inIT Date: Sun, 26 Jul 2026 16:56:28 +0700 Subject: [PATCH 5/5] test(mcp): keep scope expectations fixture-driven --- .../scripts/test-keycloak-mcp-onboarding.sh | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/infrastructure/deployment/scripts/test-keycloak-mcp-onboarding.sh b/infrastructure/deployment/scripts/test-keycloak-mcp-onboarding.sh index f884ca9a..9650477a 100755 --- a/infrastructure/deployment/scripts/test-keycloak-mcp-onboarding.sh +++ b/infrastructure/deployment/scripts/test-keycloak-mcp-onboarding.sh @@ -104,16 +104,17 @@ MSYS_NO_PATHCONV=1 docker exec "$container" \ /opt/keycloak/bin/kcadm.sh get "client-scopes/$basic_scope_id" \ -r orgmemory \ --config "$container_kcadm_config" >"$tmp_dir/basic-scope.json" -python3 - "$tmp_dir/basic-scope.json" <<'PY' +python3 \ + - "$tmp_dir/basic-scope.json" \ + "$repo_root/infrastructure/keycloak/mcp-basic-client-scope.json" <<'PY' import json import sys with open(sys.argv[1], encoding="utf-8") as stream: scope = json.load(stream) -expected_description = ( - "OpenID Connect scope for basic subject and authentication-time claims" -) -assert scope["description"] == expected_description, scope +with open(sys.argv[2], encoding="utf-8") as stream: + expected = json.load(stream) +assert scope["description"] == expected["description"], scope PY curl --fail --silent --show-error "$metadata_url" >"$tmp_dir/metadata.json"