@a5c-ai/kradle 5.0.1-staging.3abdf9534c25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Dockerfile +31 -0
- package/README.md +187 -0
- package/bin/kradle-demo.mjs +23 -0
- package/bin/kradle-server.mjs +14 -0
- package/dist/kradle-controller-ui.json +3482 -0
- package/dist/kradle-lifecycle.json +201 -0
- package/dist/kradle-runtime-snapshot.json +3125 -0
- package/dist/kradle-summary.json +724 -0
- package/docs/README.md +61 -0
- package/docs/agents/README.md +83 -0
- package/docs/agents/acceptance-test-matrix.md +193 -0
- package/docs/agents/agent-mux-adapter-contract.md +167 -0
- package/docs/agents/agent-mux-source-map.md +310 -0
- package/docs/agents/agent-run-memory-import-spec.md +256 -0
- package/docs/agents/agent-stack-management-spec.md +421 -0
- package/docs/agents/api-contract-spec.md +309 -0
- package/docs/agents/artifacts-writeback-spec.md +145 -0
- package/docs/agents/chart-packaging-spec.md +128 -0
- package/docs/agents/ci-orchestration-spec.md +140 -0
- package/docs/agents/context-assembly-spec.md +219 -0
- package/docs/agents/controller-reconciliation-spec.md +255 -0
- package/docs/agents/crd-schema-spec.md +315 -0
- package/docs/agents/decision-log-open-questions.md +169 -0
- package/docs/agents/developer-implementation-checklist.md +329 -0
- package/docs/agents/dispatching-design.md +262 -0
- package/docs/agents/gaps-agent-mux-to-kradle-crds.md +298 -0
- package/docs/agents/glossary.md +66 -0
- package/docs/agents/implementation-blueprint.md +324 -0
- package/docs/agents/implementation-rollout-slices.md +251 -0
- package/docs/agents/memory-context-integration-spec.md +194 -0
- package/docs/agents/memory-ontology-schema-spec.md +253 -0
- package/docs/agents/memory-operations-runbook.md +121 -0
- package/docs/agents/mvp-vertical-slice-spec.md +146 -0
- package/docs/agents/observability-audit-spec.md +265 -0
- package/docs/agents/operator-runbook.md +174 -0
- package/docs/agents/org-memory-api-payload-examples.md +333 -0
- package/docs/agents/org-memory-controller-sequence-spec.md +181 -0
- package/docs/agents/org-memory-e2e-fixture-plan.md +161 -0
- package/docs/agents/org-memory-ui-implementation-map.md +114 -0
- package/docs/agents/org-memory-vertical-slice-spec.md +168 -0
- package/docs/agents/org-resource-model-delta-spec.md +111 -0
- package/docs/agents/org-route-resource-model-spec.md +183 -0
- package/docs/agents/org-scoping-namespace-spec.md +114 -0
- package/docs/agents/rbac-secrets-management-spec.md +406 -0
- package/docs/agents/repository-page-integration-spec.md +255 -0
- package/docs/agents/resource-contract-examples.md +808 -0
- package/docs/agents/resource-relationship-map.md +190 -0
- package/docs/agents/security-threat-model.md +188 -0
- package/docs/agents/shared-memory-company-brain-spec.md +358 -0
- package/docs/agents/storage-migration-spec.md +168 -0
- package/docs/agents/subagent-orchestration-spec.md +152 -0
- package/docs/agents/system-overview.md +88 -0
- package/docs/agents/tools-mcp-skills-spec.md +189 -0
- package/docs/agents/traceability-matrix.md +79 -0
- package/docs/agents/ui-flow-spec.md +211 -0
- package/docs/agents/ui-ux-system-spec.md +426 -0
- package/docs/agents/workspace-lifecycle-spec.md +166 -0
- package/docs/architecture-spec.md +78 -0
- package/docs/architecture-v2.md +2759 -0
- package/docs/components/control-plane.md +78 -0
- package/docs/components/data-plane.md +69 -0
- package/docs/components/hooks-events.md +67 -0
- package/docs/components/identity-rbac-policy.md +73 -0
- package/docs/components/kubevela-oam.md +70 -0
- package/docs/components/operations-publishing.md +81 -0
- package/docs/components/runners-ci.md +66 -0
- package/docs/components/web-ui.md +94 -0
- package/docs/crd-behaviors-and-relationships.md +3926 -0
- package/docs/external/README.md +47 -0
- package/docs/external/bidirectional-sync-design.md +134 -0
- package/docs/external/cicd-interface.md +64 -0
- package/docs/external/external-backend-controllers.md +170 -0
- package/docs/external/external-backend-crds.md +234 -0
- package/docs/external/external-backend-ui-spec.md +151 -0
- package/docs/external/external-backend-ux-flows.md +115 -0
- package/docs/external/external-object-mapping.md +125 -0
- package/docs/external/git-forge-interface.md +68 -0
- package/docs/external/github-integration-design.md +151 -0
- package/docs/external/issue-tracking-interface.md +66 -0
- package/docs/external/provider-capability-manifests.md +204 -0
- package/docs/external/provider-catalog.md +139 -0
- package/docs/external/provider-rollout-testing.md +78 -0
- package/docs/external/research-results.md +48 -0
- package/docs/external/security-auth-permissions.md +81 -0
- package/docs/external/sync-state-machines.md +108 -0
- package/docs/external/unified-external-backend-model.md +107 -0
- package/docs/external/user-facing-changes.md +67 -0
- package/docs/gaps.md +161 -0
- package/docs/install.md +94 -0
- package/docs/integration-and-design-decisions.md +1530 -0
- package/docs/kradle-design.md +334 -0
- package/docs/local-minikube.md +55 -0
- package/docs/ontology/README.md +32 -0
- package/docs/ontology/bounded-contexts.md +29 -0
- package/docs/ontology/events-and-hooks.md +32 -0
- package/docs/ontology/oam-kubevela.md +32 -0
- package/docs/ontology/operations-and-release.md +25 -0
- package/docs/ontology/personas-and-actors.md +32 -0
- package/docs/ontology/policies-and-invariants.md +33 -0
- package/docs/ontology/problem-space.md +30 -0
- package/docs/ontology/resource-contracts.md +40 -0
- package/docs/ontology/resource-taxonomy.md +42 -0
- package/docs/ontology/runners-and-ci.md +29 -0
- package/docs/ontology/solution-space.md +24 -0
- package/docs/ontology/storage-and-data-boundaries.md +29 -0
- package/docs/ontology/validation-matrix.md +24 -0
- package/docs/ontology/web-ui-excellent-flows.md +32 -0
- package/docs/ontology/workflows.md +39 -0
- package/docs/ontology/world.md +35 -0
- package/docs/openapi.yaml +1291 -0
- package/docs/product-requirements.md +62 -0
- package/docs/requirements-v2.md +235 -0
- package/docs/roadmap-mvp.md +87 -0
- package/docs/sdk-api-reference.md +1108 -0
- package/docs/system-requirements.md +90 -0
- package/docs/system-spec-v2.md +1230 -0
- package/docs/tests/README.md +53 -0
- package/docs/tests/agent-qa-plan.md +63 -0
- package/docs/tests/browser-ui-tests.md +62 -0
- package/docs/tests/ci-quality-gates.md +48 -0
- package/docs/tests/coverage-model.md +64 -0
- package/docs/tests/e2e-scenario-tests.md +53 -0
- package/docs/tests/fixtures-test-data.md +63 -0
- package/docs/tests/observability-reliability-tests.md +54 -0
- package/docs/tests/product-test-matrix.md +145 -0
- package/docs/tests/qa-adoption-roadmap.md +130 -0
- package/docs/tests/qa-automation-plan.md +101 -0
- package/docs/tests/security-compliance-tests.md +57 -0
- package/docs/tests/test-framework-tools.md +88 -0
- package/docs/tests/test-suite-layout.md +121 -0
- package/docs/tests/unit-integration-tests.md +48 -0
- package/docs/todo-kyverno +714 -0
- package/docs/todos.md +4 -0
- package/docs/user-stories.md +78 -0
- package/docs/web-console-spec.md +533 -0
- package/examples/minikube-demo.yaml +190 -0
- package/examples/oam-application.yaml +23 -0
- package/examples/policy-kyverno-pr-title.yaml +18 -0
- package/package.json +66 -0
- package/scripts/build.mjs +29 -0
- package/scripts/setup-minikube.mjs +65 -0
- package/scripts/smoke.mjs +37 -0
- package/scripts/validate-doc-coverage.mjs +152 -0
- package/scripts/validate-package.mjs +95 -0
- package/scripts/validate-ui.mjs +305 -0
- package/src/agent-adapter-controller.js +169 -0
- package/src/agent-approval-controller.js +170 -0
- package/src/agent-context-bundles.js +242 -0
- package/src/agent-dispatch-controller.js +549 -0
- package/src/agent-gateway-config-controller.js +147 -0
- package/src/agent-identity-migration.js +115 -0
- package/src/agent-memory-controller.js +357 -0
- package/src/agent-memory-import.js +327 -0
- package/src/agent-memory-query.js +292 -0
- package/src/agent-memory-repository-source-controller.js +255 -0
- package/src/agent-mux-client.js +589 -0
- package/src/agent-permission-review.js +250 -0
- package/src/agent-persona-controller.js +135 -0
- package/src/agent-project-controller.js +117 -0
- package/src/agent-prompt-composition.js +55 -0
- package/src/agent-provider-config-controller.js +151 -0
- package/src/agent-secret-config-grant-controller.js +282 -0
- package/src/agent-session-transcript-controller.js +189 -0
- package/src/agent-stack-controller.js +421 -0
- package/src/agent-subagent-controller.js +160 -0
- package/src/agent-transport-binding-controller.js +121 -0
- package/src/agent-trigger-controller.js +387 -0
- package/src/agent-workspace-controller.js +702 -0
- package/src/agent-writeback-controller.js +302 -0
- package/src/api-controller.js +621 -0
- package/src/argocd-gitops.js +43 -0
- package/src/artifact-registry-controller.js +542 -0
- package/src/assistant-runtime.js +284 -0
- package/src/async-controller.js +207 -0
- package/src/audit-controller.js +191 -0
- package/src/auth.js +310 -0
- package/src/component-catalog.js +41 -0
- package/src/control-plane.js +136 -0
- package/src/controller-client.js +112 -0
- package/src/controller-ui.js +620 -0
- package/src/data-plane.js +179 -0
- package/src/event-bus.js +397 -0
- package/src/external/conflict-controller.js +225 -0
- package/src/external/github/auth.js +96 -0
- package/src/external/github/cicd.js +180 -0
- package/src/external/github/git-forge.js +240 -0
- package/src/external/github/index.js +144 -0
- package/src/external/github/issue-tracking.js +163 -0
- package/src/external/provider-adapter.js +161 -0
- package/src/external/provider-resource-factory.js +221 -0
- package/src/external/sync-controller.js +235 -0
- package/src/external/webhook-controller.js +144 -0
- package/src/external/write-controller.js +283 -0
- package/src/gitea-backend.js +131 -0
- package/src/gitea-service.js +173 -0
- package/src/handoff.js +98 -0
- package/src/health-probes.js +134 -0
- package/src/hooks-events.js +63 -0
- package/src/hooks-lifecycle.js +117 -0
- package/src/http-server.js +409 -0
- package/src/identity-policy.js +86 -0
- package/src/index.js +71 -0
- package/src/jitsi-agent-bridge.js +141 -0
- package/src/jitsi-meeting-controller.js +291 -0
- package/src/jitsi-sync-controller.js +198 -0
- package/src/kradle-inference-service-controller.js +246 -0
- package/src/kubernetes-controller-async.js +531 -0
- package/src/kubernetes-controller.js +904 -0
- package/src/kubernetes-resource-gateway.js +48 -0
- package/src/model-route-controller.js +364 -0
- package/src/notification-controller.js +178 -0
- package/src/operations.js +112 -0
- package/src/org-scoping.js +5 -0
- package/src/resource-model.js +282 -0
- package/src/runner-controller.js +272 -0
- package/src/runners-ci.js +48 -0
- package/src/runtime.js +196 -0
- package/src/snapshot-cache.js +157 -0
- package/src/virtual-model-controller.js +538 -0
- package/src/virtual-model-hook-bridge.js +200 -0
- package/src/web-ui.js +40 -0
- package/tests/agent-adapter-controller.test.js +361 -0
- package/tests/agent-approval-controller.test.js +173 -0
- package/tests/agent-context-bundles.test.js +278 -0
- package/tests/agent-dispatch-controller.test.js +679 -0
- package/tests/agent-gateway-config-controller.test.js +386 -0
- package/tests/agent-identity-migration.test.js +87 -0
- package/tests/agent-memory-controller.test.js +461 -0
- package/tests/agent-memory-import-snapshot.test.js +477 -0
- package/tests/agent-memory-query.test.js +404 -0
- package/tests/agent-memory-repository-source.test.js +514 -0
- package/tests/agent-mux-client.test.js +389 -0
- package/tests/agent-mux-integration.test.js +971 -0
- package/tests/agent-permission-review-v2.test.js +317 -0
- package/tests/agent-permission-review.test.js +209 -0
- package/tests/agent-persona-controller.test.js +127 -0
- package/tests/agent-project-controller.test.js +302 -0
- package/tests/agent-prompt-composition.test.js +76 -0
- package/tests/agent-provider-config-controller.test.js +376 -0
- package/tests/agent-resources.test.js +303 -0
- package/tests/agent-secret-config-grant.test.js +231 -0
- package/tests/agent-session-transcript-controller.test.js +499 -0
- package/tests/agent-stack-controller.test.js +283 -0
- package/tests/agent-subagent-controller.test.js +201 -0
- package/tests/agent-transport-binding-controller.test.js +294 -0
- package/tests/agent-trigger-controller.test.js +271 -0
- package/tests/agent-trigger-routes.test.js +190 -0
- package/tests/agent-trigger-sources.test.js +245 -0
- package/tests/agent-workspace-controller.test.js +181 -0
- package/tests/agent-writeback.test.js +292 -0
- package/tests/approval-persistence.test.js +171 -0
- package/tests/artifact-registry.test.js +511 -0
- package/tests/assistant-runtime.test.js +506 -0
- package/tests/async-controller.test.js +252 -0
- package/tests/audit-controller.test.js +227 -0
- package/tests/codespace-controller.test.js +318 -0
- package/tests/controller-client.test.js +133 -0
- package/tests/deployment.test.js +527 -0
- package/tests/e2e/lifecycle.test.js +120 -0
- package/tests/event-bus-integration.test.js +355 -0
- package/tests/external-github-forge.test.js +560 -0
- package/tests/external-github-issues-cicd.test.js +520 -0
- package/tests/external-integration.test.js +470 -0
- package/tests/external-persistence.test.js +415 -0
- package/tests/external-provider-adapter.test.js +365 -0
- package/tests/external-resource-model.test.js +223 -0
- package/tests/external-webhook-sync.test.js +287 -0
- package/tests/external-write-conflict.test.js +353 -0
- package/tests/gitea-service.test.js +253 -0
- package/tests/health-check-real.test.js +165 -0
- package/tests/health-probes.test.js +90 -0
- package/tests/hooks-lifecycle.test.js +364 -0
- package/tests/integration/full-flow.test.js +266 -0
- package/tests/jitsi-agent-bridge.test.js +119 -0
- package/tests/jitsi-helm-integration.test.js +77 -0
- package/tests/jitsi-meeting-controller.test.js +170 -0
- package/tests/jitsi-resource-model.test.js +73 -0
- package/tests/jitsi-sync-controller.test.js +112 -0
- package/tests/kradle-inference-service.test.js +689 -0
- package/tests/kradle.test.js +779 -0
- package/tests/memory-search-wiring.test.js +270 -0
- package/tests/model-route-controller.test.js +733 -0
- package/tests/notification-controller.test.js +196 -0
- package/tests/notification-integration.test.js +179 -0
- package/tests/org-scoping.test.js +687 -0
- package/tests/runner-controller.test.js +327 -0
- package/tests/runner-integration.test.js +231 -0
- package/tests/session-cookie-hmac.test.js +151 -0
- package/tests/snapshot-performance.test.js +315 -0
- package/tests/sse-events.test.js +107 -0
- package/tests/virtual-model-controller.test.js +877 -0
- package/tests/virtual-model-hook-bridge.test.js +384 -0
- package/tests/webhook-trigger.test.js +198 -0
- package/tests/workspace-volumes.test.js +312 -0
- package/tests/writeback-persistence.test.js +207 -0
|
@@ -0,0 +1,3926 @@
|
|
|
1
|
+
# Kradle CRD Behaviors and Relationships
|
|
2
|
+
|
|
3
|
+
Exhaustive behavioral specification for all 76 Kradle resource kinds, external backend
|
|
4
|
+
synchronization patterns, Gitea and GitHub integration, issue/project relationships,
|
|
5
|
+
and run/runner lifecycle with Argo CD and external pipeline integration.
|
|
6
|
+
|
|
7
|
+
**API Group:** `kradle.a5c.ai`
|
|
8
|
+
**API Version:** `v1alpha1`
|
|
9
|
+
**Platform Namespace:** `kradle-system` (configurable via `KRADLE_NAMESPACE`)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## PART 1: Complete CRD Behavioral Specification
|
|
14
|
+
|
|
15
|
+
This section documents every resource kind in the Kradle platform. Resources are divided
|
|
16
|
+
into two storage classes:
|
|
17
|
+
|
|
18
|
+
- **CONFIG_KINDS** (44 kinds): Stored in etcd via Kubernetes CRDs, managed declaratively
|
|
19
|
+
- **AGGREGATED_KINDS** (30 kinds): Stored in PostgreSQL, accessed via aggregated API
|
|
20
|
+
|
|
21
|
+
Additionally, the platform integrates with external CRDs:
|
|
22
|
+
- **KubeVela** (12 kinds): `core.oam.dev` group — delivery plane
|
|
23
|
+
- **Kyverno** (10 kinds): `kyverno.io` and `policies.kyverno.io` — policy engine
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
### 1.1 Identity Domain (8 kinds)
|
|
28
|
+
|
|
29
|
+
Resources that establish tenant boundaries, user identities, team membership,
|
|
30
|
+
invitations, identity federation, authentication providers, and agent service accounts.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
#### Organization
|
|
35
|
+
|
|
36
|
+
| Field | Value |
|
|
37
|
+
|-------|-------|
|
|
38
|
+
| Storage | etcd |
|
|
39
|
+
| Context | identity |
|
|
40
|
+
| Plural | organizations |
|
|
41
|
+
| Namespace | `kradle-system` (platform-scoped) |
|
|
42
|
+
|
|
43
|
+
**Purpose:** Tenant boundary. Each organization owns exactly one namespace and all
|
|
44
|
+
resources within it. Organizations are the top-level isolation primitive.
|
|
45
|
+
|
|
46
|
+
**Required Spec Fields:**
|
|
47
|
+
- `displayName` — human-readable organization name
|
|
48
|
+
- `namespaceName` — the bound Kubernetes namespace (e.g., `kradle-org-acme`)
|
|
49
|
+
|
|
50
|
+
**Derived Fields:**
|
|
51
|
+
- `spec.slug` — URL-safe identifier derived from metadata.name via `normalizeOrgSlug()`
|
|
52
|
+
|
|
53
|
+
**Behavior on Create/Apply:**
|
|
54
|
+
1. `withOrgScope()` normalizes the slug and assigns `metadata.namespace = kradle-system`
|
|
55
|
+
2. `ensureNamespace()` is called to guarantee `kradle-org-{slug}` namespace exists
|
|
56
|
+
3. Labels are applied: `kradle.a5c.ai/org: {slug}`, `kradle.a5c.ai/namespace: kradle-org-{slug}`
|
|
57
|
+
4. If the namespace does not exist, `kubectl create namespace {name}` is executed
|
|
58
|
+
|
|
59
|
+
**Behavior on Login (auto-creation):**
|
|
60
|
+
- `registerLoginProfile()` in `auth.js` reads `KRADLE_ADMIN_ORG` or `KRADLE_ORG` (default: `'default'`)
|
|
61
|
+
- Creates or updates the Organization if it does not exist
|
|
62
|
+
|
|
63
|
+
**Relationships:**
|
|
64
|
+
- Owns: ALL other org-scoped resources in `kradle-org-{slug}` namespace
|
|
65
|
+
- Referenced by: OrgNamespaceBinding (1:1 mapping)
|
|
66
|
+
- Platform scope: Organization and OrgNamespaceBinding live in `kradle-system`
|
|
67
|
+
|
|
68
|
+
**Controller:** `kubernetes-controller.js` — `organizationNamespaces()` resolves all org namespaces for snapshot enumeration
|
|
69
|
+
|
|
70
|
+
**Web Pages:** `/orgs` (list), auto-created on first OAuth login
|
|
71
|
+
|
|
72
|
+
**Reconciliation Plan:**
|
|
73
|
+
- `createOrganization()` creates three resources atomically:
|
|
74
|
+
1. Kubernetes Namespace manifest with org labels
|
|
75
|
+
2. Organization resource in `kradle-system`
|
|
76
|
+
3. OrgNamespaceBinding resource in `kradle-system`
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
#### OrgNamespaceBinding
|
|
81
|
+
|
|
82
|
+
| Field | Value |
|
|
83
|
+
|-------|-------|
|
|
84
|
+
| Storage | etcd |
|
|
85
|
+
| Context | identity |
|
|
86
|
+
| Plural | orgnamespacebindings |
|
|
87
|
+
| Namespace | `kradle-system` (platform-scoped) |
|
|
88
|
+
|
|
89
|
+
**Purpose:** Explicit binding from one Organization to exactly one tenant namespace.
|
|
90
|
+
Ensures that namespace ownership is auditable and that organizations cannot accidentally
|
|
91
|
+
share namespaces.
|
|
92
|
+
|
|
93
|
+
**Required Spec Fields:**
|
|
94
|
+
- `organizationRef` — reference to the Organization slug
|
|
95
|
+
- `namespace` — the target namespace name
|
|
96
|
+
|
|
97
|
+
**Behavior on Create/Apply:**
|
|
98
|
+
1. `withOrgScope()` resolves org slug → namespace name
|
|
99
|
+
2. `spec.createNamespace` defaults to `true`
|
|
100
|
+
3. Labels propagated: `kradle.a5c.ai/org`, `kradle.a5c.ai/namespace`
|
|
101
|
+
4. Additional labels from `spec.labels` are merged into namespace metadata
|
|
102
|
+
|
|
103
|
+
**Relationships:**
|
|
104
|
+
- 1:1 with Organization
|
|
105
|
+
- Determines which namespaces are scanned during `getControllerSnapshot()`
|
|
106
|
+
- Used by `organizationNamespaces()` to build the list of org-scoped namespaces
|
|
107
|
+
|
|
108
|
+
**Reconciliation:**
|
|
109
|
+
- Created atomically alongside Organization via `createOrganization()`
|
|
110
|
+
- Snapshot controller enumerates bindings to discover all tenant namespaces
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
#### User
|
|
115
|
+
|
|
116
|
+
| Field | Value |
|
|
117
|
+
|-------|-------|
|
|
118
|
+
| Storage | etcd |
|
|
119
|
+
| Context | identity |
|
|
120
|
+
| Plural | users |
|
|
121
|
+
| Namespace | org-scoped (`kradle-org-{slug}`) |
|
|
122
|
+
|
|
123
|
+
**Purpose:** Human account profile representing an organization member. Tracks sign-in
|
|
124
|
+
state, admin privileges, team membership, and linked external identities.
|
|
125
|
+
|
|
126
|
+
**Required Spec Fields:**
|
|
127
|
+
- `organizationRef` — owning organization
|
|
128
|
+
- `displayName` — full name
|
|
129
|
+
- `email` — primary email address
|
|
130
|
+
|
|
131
|
+
**Optional Spec Fields:**
|
|
132
|
+
- `username` — login handle
|
|
133
|
+
- `teams[]` — team membership list
|
|
134
|
+
- `groups[]` — group membership (e.g., `kradle:platform-engineers`)
|
|
135
|
+
- `disabled` — boolean to suspend the user
|
|
136
|
+
- `admin` — boolean for platform admin privileges
|
|
137
|
+
|
|
138
|
+
**Behavior on Create (via OAuth):**
|
|
139
|
+
1. `exchangeOAuthCodeForProfile()` exchanges authorization code for access token
|
|
140
|
+
2. `normalizeProviderProfile()` maps provider-specific fields to canonical form
|
|
141
|
+
3. `registerLoginProfile()` calls `mapLoginProfileToKradleIdentity()` which:
|
|
142
|
+
- Creates or updates a User resource
|
|
143
|
+
- Creates or updates an IdentityMapping resource
|
|
144
|
+
4. Bootstrap admin detection: if `KRADLE_ADMIN_USERNAME` matches, `admin: true`
|
|
145
|
+
|
|
146
|
+
**Reconciliation (identity access):**
|
|
147
|
+
- `identityAccessReconciliationPlan()` for User kind:
|
|
148
|
+
- Phase: `Active` (normal) or `Disabled` (suspended)
|
|
149
|
+
- Computes `repositoryIdentity` from username or metadata.name
|
|
150
|
+
- Computes `groups` array: `['kradle:users', role-group, ...team-groups]`
|
|
151
|
+
- Sync intents:
|
|
152
|
+
- `workspace-identity`: ensure-user or suspend-user
|
|
153
|
+
- `repository-access`: ensure-repository-user or suspend-repository-user
|
|
154
|
+
|
|
155
|
+
**RBAC:**
|
|
156
|
+
- Label `role: admin|member` determines permission level
|
|
157
|
+
- Admin users get group `kradle:platform-engineers`
|
|
158
|
+
- Non-admin users get group `kradle:developers`
|
|
159
|
+
|
|
160
|
+
**Relationships:**
|
|
161
|
+
- Belongs to: Organization (via organizationRef)
|
|
162
|
+
- Has: IdentityMapping (1:N, one per provider)
|
|
163
|
+
- References: Team (via teams[] array)
|
|
164
|
+
- Referenced by: RepositoryPermission, AgentApproval, AgentDispatchRun
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
#### Team
|
|
169
|
+
|
|
170
|
+
| Field | Value |
|
|
171
|
+
|-------|-------|
|
|
172
|
+
| Storage | etcd |
|
|
173
|
+
| Context | identity |
|
|
174
|
+
| Plural | teams |
|
|
175
|
+
| Namespace | org-scoped |
|
|
176
|
+
|
|
177
|
+
**Purpose:** Team membership group with maintainers and repository permission grants.
|
|
178
|
+
Teams provide a grouping mechanism for repository access control.
|
|
179
|
+
|
|
180
|
+
**Required Spec Fields:**
|
|
181
|
+
- `organizationRef` — owning organization
|
|
182
|
+
- `displayName` — team name
|
|
183
|
+
|
|
184
|
+
**Optional Spec Fields:**
|
|
185
|
+
- `members[]` — list of user references
|
|
186
|
+
- `maintainers[]` — subset of members with team management rights
|
|
187
|
+
- `repositoryGrants[]` — array of `{ repository, permission }` tuples
|
|
188
|
+
|
|
189
|
+
**Reconciliation:**
|
|
190
|
+
- Phase: always `Active`
|
|
191
|
+
- Reports `memberCount` and `maintainerCount`
|
|
192
|
+
- Sync intents:
|
|
193
|
+
- `workspace-identity`: sync-team-membership (members + maintainers)
|
|
194
|
+
- `repository-access`: one sync-team-repository-grant per repositoryGrant entry
|
|
195
|
+
- Conditions: `TeamMembershipProjected`, `RepositoryGrantsProjected`
|
|
196
|
+
|
|
197
|
+
**Relationships:**
|
|
198
|
+
- Belongs to: Organization
|
|
199
|
+
- Contains: Users (via members/maintainers arrays)
|
|
200
|
+
- Grants: RepositoryPermission (via repositoryGrants)
|
|
201
|
+
- Synced to: Gitea team (via `createTeam()`, `addTeamMember()`, `addTeamRepository()`)
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
#### Invite
|
|
206
|
+
|
|
207
|
+
| Field | Value |
|
|
208
|
+
|-------|-------|
|
|
209
|
+
| Storage | etcd |
|
|
210
|
+
| Context | identity |
|
|
211
|
+
| Plural | invites |
|
|
212
|
+
| Namespace | org-scoped |
|
|
213
|
+
|
|
214
|
+
**Purpose:** Pending user invitation with requested teams and expiry. Tracks the
|
|
215
|
+
invitation lifecycle from creation through acceptance or expiration.
|
|
216
|
+
|
|
217
|
+
**Required Spec Fields:**
|
|
218
|
+
- `organizationRef` — owning organization
|
|
219
|
+
- `email` — invitee email address
|
|
220
|
+
- `role` — requested role (admin/member)
|
|
221
|
+
|
|
222
|
+
**Optional Spec Fields:**
|
|
223
|
+
- `expiresAt` — ISO 8601 expiration timestamp
|
|
224
|
+
- `teams[]` — teams to auto-assign on acceptance
|
|
225
|
+
|
|
226
|
+
**Lifecycle Phases:**
|
|
227
|
+
- `Pending` — invitation sent, awaiting acceptance
|
|
228
|
+
- `Accepted` — user has accepted; User resource created
|
|
229
|
+
- `Expired` — past expiresAt without acceptance
|
|
230
|
+
- `Revoked` — manually cancelled by admin
|
|
231
|
+
|
|
232
|
+
**Reconciliation:**
|
|
233
|
+
- Tracks `expiresAt` in status
|
|
234
|
+
- Sync intents:
|
|
235
|
+
- `workspace-identity`: send-invite (Pending) or close-invite (other phases)
|
|
236
|
+
- Condition: `InviteLifecycleTracked`
|
|
237
|
+
|
|
238
|
+
**Relationships:**
|
|
239
|
+
- Belongs to: Organization
|
|
240
|
+
- Creates: User (on acceptance)
|
|
241
|
+
- References: Teams (for auto-assignment)
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
#### IdentityMapping
|
|
246
|
+
|
|
247
|
+
| Field | Value |
|
|
248
|
+
|-------|-------|
|
|
249
|
+
| Storage | etcd |
|
|
250
|
+
| Context | identity |
|
|
251
|
+
| Plural | identitymappings |
|
|
252
|
+
| Namespace | org-scoped |
|
|
253
|
+
|
|
254
|
+
**Purpose:** Mapping between Kradle user accounts, sign-in provider subjects, workspace
|
|
255
|
+
identities, and repository hosting accounts. Enables federation across multiple
|
|
256
|
+
identity providers.
|
|
257
|
+
|
|
258
|
+
**Required Spec Fields:**
|
|
259
|
+
- `organizationRef` — owning organization
|
|
260
|
+
- `user` — reference to local User resource name
|
|
261
|
+
- `provider` — identity provider ID (e.g., 'github', 'sso', 'delegated')
|
|
262
|
+
- `subject` — external subject identifier (e.g., GitHub user ID)
|
|
263
|
+
|
|
264
|
+
**Optional Spec Fields:**
|
|
265
|
+
- `workspaceIdentity` — `{ name }` workspace identity binding
|
|
266
|
+
- `repositoryIdentity` — `{ username }` git hosting identity binding
|
|
267
|
+
|
|
268
|
+
**Behavior on Create:**
|
|
269
|
+
- Created automatically during OAuth login callback
|
|
270
|
+
- `mapLoginProfileToKradleIdentity()` in `identity-policy.js` generates both User and IdentityMapping
|
|
271
|
+
|
|
272
|
+
**Reconciliation:**
|
|
273
|
+
- Phase: `Synced` (all fields present) or `Pending` (missing user/provider/subject)
|
|
274
|
+
- Reports `workspaceIdentity` and `repositoryIdentity` in status
|
|
275
|
+
- Sync intents:
|
|
276
|
+
- `workspace-identity`: link-identity (user, provider, subject)
|
|
277
|
+
- `repository-access`: link-repository-identity (user, repositoryIdentity)
|
|
278
|
+
- Conditions: `WorkspaceIdentityProjected`, `RepositoryIdentityProjected`
|
|
279
|
+
|
|
280
|
+
**Relationships:**
|
|
281
|
+
- Belongs to: User (via user field)
|
|
282
|
+
- Links: external provider subject → local user
|
|
283
|
+
- Used by: auth system to resolve login → User mapping
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
#### AuthProvider
|
|
288
|
+
|
|
289
|
+
| Field | Value |
|
|
290
|
+
|-------|-------|
|
|
291
|
+
| Storage | etcd |
|
|
292
|
+
| Context | identity |
|
|
293
|
+
| Plural | authproviders |
|
|
294
|
+
| Namespace | org-scoped |
|
|
295
|
+
|
|
296
|
+
**Purpose:** Installation sign-in provider configuration including visibility, OAuth
|
|
297
|
+
endpoints, and delegated identity settings. Controls which authentication methods
|
|
298
|
+
are available.
|
|
299
|
+
|
|
300
|
+
**Required Spec Fields:**
|
|
301
|
+
- `organizationRef` — owning organization
|
|
302
|
+
- `type` — provider type ('github', 'oidc', 'delegated')
|
|
303
|
+
|
|
304
|
+
**Optional Spec Fields (via environment):**
|
|
305
|
+
- `clientId` — OAuth client ID
|
|
306
|
+
- `clientSecret` — OAuth client secret (never stored in spec, env-only)
|
|
307
|
+
- `authorizationUrl` — OAuth authorization endpoint
|
|
308
|
+
- `tokenUrl` — OAuth token exchange endpoint
|
|
309
|
+
- `userInfoUrl` — OIDC userinfo endpoint
|
|
310
|
+
- `scopes` — space-separated OAuth scopes
|
|
311
|
+
- `enabled` — boolean toggle
|
|
312
|
+
|
|
313
|
+
**Provider Types:**
|
|
314
|
+
1. **GitHub OAuth**: `KRADLE_AUTH_GITHUB_*` environment variables
|
|
315
|
+
2. **SSO (OIDC)**: `KRADLE_AUTH_SSO_*` environment variables
|
|
316
|
+
3. **Delegated Identity**: proxy-header-based auth (`x-forwarded-user`, etc.)
|
|
317
|
+
|
|
318
|
+
**Configuration (via `createAuthProviderConfig()`):**
|
|
319
|
+
- Session cookie name: `KRADLE_AUTH_COOKIE_NAME` (default: `kradle_session`)
|
|
320
|
+
- Delegated identity local development mode for testing without real OAuth
|
|
321
|
+
- Session secret for HMAC-signed cookies: `KRADLE_SESSION_SECRET`
|
|
322
|
+
|
|
323
|
+
**Relationships:**
|
|
324
|
+
- Used by: auth middleware, login flow
|
|
325
|
+
- Produces: IdentityMapping (on successful login)
|
|
326
|
+
- Referenced by: IdentityMapping.spec.provider
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
#### AgentServiceAccount
|
|
331
|
+
|
|
332
|
+
| Field | Value |
|
|
333
|
+
|-------|-------|
|
|
334
|
+
| Storage | etcd |
|
|
335
|
+
| Context | identity |
|
|
336
|
+
| Plural | agentserviceaccounts |
|
|
337
|
+
| Namespace | org-scoped |
|
|
338
|
+
|
|
339
|
+
**Purpose:** Kubernetes ServiceAccount wrapper for agent and runner identity binding.
|
|
340
|
+
Provides the runtime identity that agent pods use to authenticate to the Kubernetes
|
|
341
|
+
API and external services.
|
|
342
|
+
|
|
343
|
+
**Required Spec Fields:**
|
|
344
|
+
- `organizationRef` — owning organization
|
|
345
|
+
- `namespace` — target K8s namespace for the ServiceAccount
|
|
346
|
+
- `serviceAccountName` — name of the K8s ServiceAccount to bind
|
|
347
|
+
|
|
348
|
+
**Behavior:**
|
|
349
|
+
- Referenced by AgentStack via `spec.runtimeIdentity.serviceAccountRef`
|
|
350
|
+
- Stack reconciliation checks `RuntimeIdentityReady` condition against this resource
|
|
351
|
+
- Used to generate pod specs with `serviceAccountName` field
|
|
352
|
+
|
|
353
|
+
**Relationships:**
|
|
354
|
+
- Referenced by: AgentStack (runtimeIdentity)
|
|
355
|
+
- Controls: RBAC access for agent pods via AgentRoleBinding
|
|
356
|
+
- Bound to: Kubernetes ServiceAccount in the cluster
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
### 1.2 Repository Domain (7 kinds)
|
|
361
|
+
|
|
362
|
+
Resources managing git repository abstractions, deploy keys, collaborator permissions,
|
|
363
|
+
branch protection rules, reference policies, webhook subscriptions, and runner pools.
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
#### Repository
|
|
368
|
+
|
|
369
|
+
| Field | Value |
|
|
370
|
+
|-------|-------|
|
|
371
|
+
| Storage | etcd |
|
|
372
|
+
| Context | data-plane |
|
|
373
|
+
| Plural | repositories |
|
|
374
|
+
| Namespace | org-scoped |
|
|
375
|
+
|
|
376
|
+
**Purpose:** Git repository abstraction over the Gitea backend (or external git hosts).
|
|
377
|
+
Manages repository identity, visibility, default branch, and integration with the
|
|
378
|
+
repository hosting layer.
|
|
379
|
+
|
|
380
|
+
**Required Spec Fields:**
|
|
381
|
+
- `organizationRef` — owning organization
|
|
382
|
+
- `visibility` — one of `public`, `internal`, `private`
|
|
383
|
+
|
|
384
|
+
**Optional Spec Fields:**
|
|
385
|
+
- `defaultBranch` — default branch name (default: `main`)
|
|
386
|
+
|
|
387
|
+
**Behavior on Create/Apply:**
|
|
388
|
+
1. `repositoryManifest()` constructs the resource with org scope
|
|
389
|
+
2. `applyResource()` calls `withOrgScope()` to resolve namespace
|
|
390
|
+
3. `ensureNamespace()` ensures the org namespace exists
|
|
391
|
+
4. `kubectl apply -f -` persists the CRD
|
|
392
|
+
5. Reconciler emits sync intent: `ensure-gitea-repository`
|
|
393
|
+
|
|
394
|
+
**Gitea Integration:**
|
|
395
|
+
- `createGiteaBackend().createRepository({ owner, name, private, defaultBranch })`
|
|
396
|
+
- Owner is the Gitea organization corresponding to the Kradle org
|
|
397
|
+
- Private flag derived from visibility (private/internal → private, public → public)
|
|
398
|
+
- Repository name matches `metadata.name`
|
|
399
|
+
|
|
400
|
+
**External Backend Integration:**
|
|
401
|
+
- When an ExternalBackendBinding exists with matching repository scope:
|
|
402
|
+
- Repository metadata synced bidirectionally with GitHub/GitLab
|
|
403
|
+
- ExternalObjectLink created mapping local repo → external repo ID
|
|
404
|
+
- Webhook registered on external provider for push/PR events
|
|
405
|
+
|
|
406
|
+
**Reconciliation (via `reconcileRepository()`):**
|
|
407
|
+
- Phase: `Reconciling` → `Ready`
|
|
408
|
+
- `gitBackend: 'gitea'` in status
|
|
409
|
+
- Conditions: `ResourceObserved`, `DataPlaneSyncPlanned`
|
|
410
|
+
- Sync intents:
|
|
411
|
+
- `git-data-plane`: ensure-gitea-repository
|
|
412
|
+
- `policy-controller`: compile-ref-policy
|
|
413
|
+
|
|
414
|
+
**Relationships:**
|
|
415
|
+
- Belongs to: Organization
|
|
416
|
+
- Has: SSHKey, RepositoryPermission, BranchProtection, RefPolicy
|
|
417
|
+
- Referenced by: AgentDispatchRun, Pipeline, Issue, PullRequest, KradleWorkspace
|
|
418
|
+
- Synced to: Gitea repository, GitHub repository (via ExternalBackendBinding)
|
|
419
|
+
|
|
420
|
+
**Web Pages:** `/repositories` (list), `/repositories/{name}/code` (browser),
|
|
421
|
+
`/repositories/{name}/settings`
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
#### SSHKey
|
|
426
|
+
|
|
427
|
+
| Field | Value |
|
|
428
|
+
|-------|-------|
|
|
429
|
+
| Storage | etcd |
|
|
430
|
+
| Context | data-plane |
|
|
431
|
+
| Plural | sshkeys |
|
|
432
|
+
| Namespace | org-scoped |
|
|
433
|
+
|
|
434
|
+
**Purpose:** User, deploy, and automation SSH keys reconciled into repository key APIs.
|
|
435
|
+
Manages the lifecycle of SSH keys used for git authentication.
|
|
436
|
+
|
|
437
|
+
**Required Spec Fields:**
|
|
438
|
+
- `organizationRef` — owning organization
|
|
439
|
+
- `scope` — key scope (e.g., 'deploy', 'user', 'automation')
|
|
440
|
+
- `key` — public key material (SSH format)
|
|
441
|
+
|
|
442
|
+
**Optional Spec Fields:**
|
|
443
|
+
- `readOnly` — boolean (default: false for user keys, true for deploy keys)
|
|
444
|
+
- `owner` / `user` — the user or automation that owns this key
|
|
445
|
+
- `revoked` — boolean to mark key as revoked
|
|
446
|
+
|
|
447
|
+
**Reconciliation:**
|
|
448
|
+
- Phase: `Synced` (active) or `Revoked` (disabled)
|
|
449
|
+
- Fingerprint computed: `sha256:{base64url hash of key material}`
|
|
450
|
+
- Sync intents:
|
|
451
|
+
- `repository-access`: sync-ssh-key or revoke-ssh-key
|
|
452
|
+
- Condition: `SSHKeyProjected`
|
|
453
|
+
|
|
454
|
+
**Gitea Integration:**
|
|
455
|
+
- `createGiteaBackend().addDeployKey({ owner, repo, title, key, readOnly })`
|
|
456
|
+
- `createGiteaBackend().addUserSshKey({ title, key, readOnly })`
|
|
457
|
+
- Deploy keys are scoped to specific repositories
|
|
458
|
+
- User keys provide access across all repositories the user can access
|
|
459
|
+
|
|
460
|
+
**External (GitHub) Integration:**
|
|
461
|
+
- `GitHubGitForge.syncDeployKeys({ repo, desiredKeys })` — adds missing, removes extra
|
|
462
|
+
- Bidirectional sync: keys created in Kradle → pushed to GitHub; keys from GitHub → synced to Kradle
|
|
463
|
+
|
|
464
|
+
**Relationships:**
|
|
465
|
+
- Belongs to: Organization, optionally scoped to a Repository
|
|
466
|
+
- Synced to: Gitea deploy keys, GitHub deploy keys
|
|
467
|
+
|
|
468
|
+
---
|
|
469
|
+
|
|
470
|
+
#### RepositoryPermission
|
|
471
|
+
|
|
472
|
+
| Field | Value |
|
|
473
|
+
|-------|-------|
|
|
474
|
+
| Storage | etcd |
|
|
475
|
+
| Context | data-plane |
|
|
476
|
+
| Plural | repositorypermissions |
|
|
477
|
+
| Namespace | org-scoped |
|
|
478
|
+
|
|
479
|
+
**Purpose:** Repository collaborator and team access permissions synced with the
|
|
480
|
+
repository hosting backend.
|
|
481
|
+
|
|
482
|
+
**Required Spec Fields:**
|
|
483
|
+
- `organizationRef` — owning organization
|
|
484
|
+
- `repository` — target repository name
|
|
485
|
+
- `subject` — user or team name
|
|
486
|
+
- `permission` — access level: `read`, `write`, `admin`
|
|
487
|
+
|
|
488
|
+
**Optional Spec Fields:**
|
|
489
|
+
- `subjectKind` — `user` or `team` (default: `user`)
|
|
490
|
+
- `revoked` — boolean to revoke access
|
|
491
|
+
|
|
492
|
+
**Reconciliation:**
|
|
493
|
+
- Phase: `Synced` (active) or `Revoked` (disabled)
|
|
494
|
+
- Reports `repository`, `subject`, `permission` in status
|
|
495
|
+
- Sync intents:
|
|
496
|
+
- `repository-access`: sync-repository-permission or revoke-repository-permission
|
|
497
|
+
- Condition: `RepositoryPermissionProjected`
|
|
498
|
+
|
|
499
|
+
**Gitea Integration:**
|
|
500
|
+
- `createGiteaBackend().addCollaborator({ owner, repo, username, permission })`
|
|
501
|
+
- `createGiteaBackend().addTeamRepository({ org, team, repo, permission })`
|
|
502
|
+
- Permission levels map directly to Gitea collaborator permissions
|
|
503
|
+
|
|
504
|
+
**External (GitHub) Integration:**
|
|
505
|
+
- Collaborators synced via GitHub REST API when ExternalBackendBinding exists
|
|
506
|
+
- Permission mapping: read→pull, write→push, admin→admin
|
|
507
|
+
|
|
508
|
+
**Relationships:**
|
|
509
|
+
- Belongs to: Organization, Repository
|
|
510
|
+
- References: User or Team (via subject)
|
|
511
|
+
- Synced to: Gitea collaborators, GitHub collaborators
|
|
512
|
+
|
|
513
|
+
---
|
|
514
|
+
|
|
515
|
+
#### BranchProtection
|
|
516
|
+
|
|
517
|
+
| Field | Value |
|
|
518
|
+
|-------|-------|
|
|
519
|
+
| Storage | etcd |
|
|
520
|
+
| Context | control-plane |
|
|
521
|
+
| Plural | branchprotections |
|
|
522
|
+
| Namespace | org-scoped |
|
|
523
|
+
|
|
524
|
+
**Purpose:** Protected reference rules such as required reviews, status checks,
|
|
525
|
+
force-push policy, and merge requirements.
|
|
526
|
+
|
|
527
|
+
**Required Spec Fields:**
|
|
528
|
+
- `organizationRef` — owning organization
|
|
529
|
+
- `refs` — branch pattern(s) to protect (e.g., `['main', 'release/*']`)
|
|
530
|
+
|
|
531
|
+
**Optional Spec Fields:**
|
|
532
|
+
- `requiredReviews` — number of required approving reviews (default: 1)
|
|
533
|
+
- `statusChecks[]` — required status check contexts
|
|
534
|
+
- `allowForcePush` — boolean (default: false)
|
|
535
|
+
- `dismissStaleReviews` — boolean
|
|
536
|
+
- `enforceAdmins` — boolean
|
|
537
|
+
|
|
538
|
+
**Gitea Integration:**
|
|
539
|
+
- `createGiteaBackend().protectBranch({ owner, repo, branch, approvals, statusChecks })`
|
|
540
|
+
- Maps to Gitea branch protection rules with push whitelist, required approvals
|
|
541
|
+
|
|
542
|
+
**External (GitHub) Integration:**
|
|
543
|
+
- `GitHubGitForge.syncBranchProtection({ repo, branch, requiredReviews, requiredStatusChecks, dismissStaleReviews, enforceAdmins })`
|
|
544
|
+
- Full bidirectional sync with GitHub branch protection rules
|
|
545
|
+
|
|
546
|
+
**Relationships:**
|
|
547
|
+
- Belongs to: Organization, implicitly scoped to Repository
|
|
548
|
+
- Enforced by: Gitea, GitHub (via sync)
|
|
549
|
+
- Referenced by: CI pipeline gates
|
|
550
|
+
|
|
551
|
+
---
|
|
552
|
+
|
|
553
|
+
#### RefPolicy
|
|
554
|
+
|
|
555
|
+
| Field | Value |
|
|
556
|
+
|-------|-------|
|
|
557
|
+
| Storage | etcd |
|
|
558
|
+
| Context | data-plane |
|
|
559
|
+
| Plural | refpolicies |
|
|
560
|
+
| Namespace | org-scoped |
|
|
561
|
+
|
|
562
|
+
**Purpose:** Reference deny rules, force-push policy, signing requirements, and custom
|
|
563
|
+
hook gates. Provides fine-grained control over what operations are allowed on refs.
|
|
564
|
+
|
|
565
|
+
**Required Spec Fields:**
|
|
566
|
+
- `organizationRef` — owning organization
|
|
567
|
+
|
|
568
|
+
**Optional Spec Fields:**
|
|
569
|
+
- `denyRules[]` — patterns that are denied (e.g., `['refs/heads/main']`)
|
|
570
|
+
- `forcePushPolicy` — `deny` | `allow` | `allowWithReview`
|
|
571
|
+
- `signingPolicy` — `required` | `optional` | `disabled`
|
|
572
|
+
- `hookGates[]` — custom pre-receive hook configurations
|
|
573
|
+
|
|
574
|
+
**Behavior:**
|
|
575
|
+
- Reconciler emits sync intent: `policy-controller: compile-ref-policy`
|
|
576
|
+
- Compiled into server-side hooks on the git backend
|
|
577
|
+
- Evaluated on push operations before accepting commits
|
|
578
|
+
|
|
579
|
+
**Relationships:**
|
|
580
|
+
- Belongs to: Organization, optionally scoped to Repository
|
|
581
|
+
- Complements: BranchProtection (ref policies are lower-level)
|
|
582
|
+
- Evaluated by: git pre-receive hooks
|
|
583
|
+
|
|
584
|
+
---
|
|
585
|
+
|
|
586
|
+
#### WebhookSubscription
|
|
587
|
+
|
|
588
|
+
| Field | Value |
|
|
589
|
+
|-------|-------|
|
|
590
|
+
| Storage | etcd |
|
|
591
|
+
| Context | hooks-events |
|
|
592
|
+
| Plural | webhooksubscriptions |
|
|
593
|
+
| Namespace | org-scoped |
|
|
594
|
+
|
|
595
|
+
**Purpose:** Outbound webhook endpoint configuration with event filters, signing
|
|
596
|
+
reference, delivery mode, and retry policy.
|
|
597
|
+
|
|
598
|
+
**Required Spec Fields:**
|
|
599
|
+
- `organizationRef` — owning organization
|
|
600
|
+
- `url` — target webhook URL
|
|
601
|
+
- `events` — array of event types to subscribe to
|
|
602
|
+
|
|
603
|
+
**Optional Spec Fields:**
|
|
604
|
+
- `secret` — shared secret for HMAC signing
|
|
605
|
+
- `contentType` — `json` (default) or `form`
|
|
606
|
+
- `active` — boolean toggle
|
|
607
|
+
- `retryPolicy` — `{ maxRetries, backoffMs }`
|
|
608
|
+
|
|
609
|
+
**Gitea Integration:**
|
|
610
|
+
- `createGiteaBackend().createWebhook({ owner, repo, url, events, secret })`
|
|
611
|
+
- Registered on the Gitea repository for push/PR/issue events
|
|
612
|
+
|
|
613
|
+
**Relationships:**
|
|
614
|
+
- Belongs to: Organization, optionally scoped to Repository
|
|
615
|
+
- Produces: WebhookDelivery records (outbound attempts)
|
|
616
|
+
- Consumed by: WebhookController for outbound delivery
|
|
617
|
+
|
|
618
|
+
---
|
|
619
|
+
|
|
620
|
+
#### RunnerPool
|
|
621
|
+
|
|
622
|
+
| Field | Value |
|
|
623
|
+
|-------|-------|
|
|
624
|
+
| Storage | etcd |
|
|
625
|
+
| Context | runners-ci |
|
|
626
|
+
| Plural | runnerpools |
|
|
627
|
+
| Namespace | org-scoped |
|
|
628
|
+
|
|
629
|
+
**Purpose:** Runner capacity pool with warm/max replicas, container image, cache policy,
|
|
630
|
+
trust boundary, and scaling configuration.
|
|
631
|
+
|
|
632
|
+
**Required Spec Fields:**
|
|
633
|
+
- `organizationRef` — owning organization
|
|
634
|
+
- `warmReplicas` — minimum number of idle runners to maintain (non-negative integer)
|
|
635
|
+
- `maxReplicas` — maximum runners allowed (positive integer, must be >= warmReplicas)
|
|
636
|
+
|
|
637
|
+
**Optional Spec Fields:**
|
|
638
|
+
- `image` — container image for runners (default: `ubuntu:24.04`)
|
|
639
|
+
- `trustTier` — `trusted` or `untrusted`
|
|
640
|
+
- `cache` — `{ type: 'object-storage' }` cache configuration
|
|
641
|
+
- `scalingMetric` — `queueDepth` (default)
|
|
642
|
+
- `serviceAccount` — K8s service account name for runners
|
|
643
|
+
- `resourceLimits` — `{ cpu, memory }` pod resource limits
|
|
644
|
+
- `resourceRequests` — `{ cpu, memory }` pod resource requests
|
|
645
|
+
|
|
646
|
+
**Validation (`validateRunnerPool()`):**
|
|
647
|
+
- metadata.name required
|
|
648
|
+
- organizationRef required
|
|
649
|
+
- warmReplicas: non-negative integer
|
|
650
|
+
- maxReplicas: positive integer >= warmReplicas
|
|
651
|
+
|
|
652
|
+
**Pool Status (`getPoolStatus()`):**
|
|
653
|
+
- `phase`: Empty | Active | Idle
|
|
654
|
+
- `scaling`: ScalingUp | ScalingDown | Stable
|
|
655
|
+
- Tracks: idle, active, terminating, total runner counts
|
|
656
|
+
|
|
657
|
+
**Capacity (`getCapacity()`):**
|
|
658
|
+
- `used`: runners currently Running
|
|
659
|
+
- `available`: maxReplicas - used
|
|
660
|
+
- `utilizationPct`: percentage of capacity in use
|
|
661
|
+
|
|
662
|
+
**Pod Spec Generation (`generatePodSpec()`):**
|
|
663
|
+
- Container: runner image with env vars (KRADLE_ORG, KRADLE_RUN_ID, KRADLE_WORKSPACE_PATH)
|
|
664
|
+
- Volumes: workspace PVC mounted at /workspace
|
|
665
|
+
- Labels: kradle.a5c.ai/runner, kradle.a5c.ai/pool, kradle.a5c.ai/org
|
|
666
|
+
- Service account: configured or default `kradle-runner`
|
|
667
|
+
- Restart policy: Never
|
|
668
|
+
|
|
669
|
+
**Relationships:**
|
|
670
|
+
- Belongs to: Organization
|
|
671
|
+
- Contains: Runners (in-memory registry)
|
|
672
|
+
- Schedules: CI Jobs from Pipelines; agent execution via `batch/v1` K8s Jobs for AgentDispatchRuns
|
|
673
|
+
- Referenced by: AgentStack (runner policy)
|
|
674
|
+
|
|
675
|
+
---
|
|
676
|
+
|
|
677
|
+
### 1.2.1 How K8s Jobs Relate to RunnerPools
|
|
678
|
+
|
|
679
|
+
Agent dispatch (via `AgentDispatchRun`) uses **Kubernetes `batch/v1` Jobs** for
|
|
680
|
+
execution, not the RunnerPool's in-memory runner registry directly.
|
|
681
|
+
|
|
682
|
+
| Concept | RunnerPool (CI) | Agent K8s Job |
|
|
683
|
+
|---------|----------------|---------------|
|
|
684
|
+
| Scheduling unit | Runner pod (long-lived, warm) | Job pod (short-lived, per-run) |
|
|
685
|
+
| Lifecycle | Idle → Running → Terminating (reused) | Pending → Active → Succeeded/Failed (one-shot) |
|
|
686
|
+
| Scaling | warmReplicas/maxReplicas | One Job per dispatch run |
|
|
687
|
+
| Workspace mount | `/workspace` via PVC | `/workspace` via same PVC pattern |
|
|
688
|
+
| Budget enforcement | `resourceLimits` in pod spec | `activeDeadlineSeconds` in Job spec |
|
|
689
|
+
| Result delivery | Runner calls back or exits | Agent pod POSTs to callback endpoint |
|
|
690
|
+
|
|
691
|
+
RunnerPool capacity is used for **CI Jobs** (Pipeline, Job resources). For **agent
|
|
692
|
+
dispatch**, the dispatch controller creates `batch/v1` Jobs directly using the pod
|
|
693
|
+
spec from the stack's `AgentServiceAccount` and `RunnerPool.spec.image` as a
|
|
694
|
+
starting point. The RunnerPool's `warmReplicas` does not pre-warm agent Job pods.
|
|
695
|
+
|
|
696
|
+
When an AgentStack references a `runnerPolicy.runnerPoolRef`, the referenced pool's
|
|
697
|
+
`spec.image`, `spec.resourceLimits`, `spec.resourceRequests`, and `spec.serviceAccount`
|
|
698
|
+
fields are used as defaults when building the agent Job manifest via `createAgentJob()`.
|
|
699
|
+
|
|
700
|
+
---
|
|
701
|
+
|
|
702
|
+
### 1.3 Policy Domain (4 kinds)
|
|
703
|
+
|
|
704
|
+
Resources that manage policy posture, template libraries, binding/enforcement,
|
|
705
|
+
and exception workflows via Kyverno integration.
|
|
706
|
+
|
|
707
|
+
---
|
|
708
|
+
|
|
709
|
+
#### PolicyProfile
|
|
710
|
+
|
|
711
|
+
| Field | Value |
|
|
712
|
+
|-------|-------|
|
|
713
|
+
| Storage | etcd |
|
|
714
|
+
| Context | policy |
|
|
715
|
+
| Plural | policyprofiles |
|
|
716
|
+
| Namespace | org-scoped |
|
|
717
|
+
|
|
718
|
+
**Purpose:** Organization-level policy posture configuration. Defines default templates,
|
|
719
|
+
rollout mode (audit vs enforce), and exception approval rules.
|
|
720
|
+
|
|
721
|
+
**Required Spec Fields:**
|
|
722
|
+
- `organizationRef` — owning organization
|
|
723
|
+
- `displayName` — profile name
|
|
724
|
+
- `mode` — rollout mode: `audit`, `enforce`, `disabled`
|
|
725
|
+
|
|
726
|
+
**Optional Spec Fields:**
|
|
727
|
+
- `defaultTemplates[]` — PolicyTemplate references to apply by default
|
|
728
|
+
- `exceptionApprovalPolicy` — rules for approving PolicyExceptionRequests
|
|
729
|
+
- `rolloutSchedule` — when to transition from audit to enforce
|
|
730
|
+
|
|
731
|
+
**Relationships:**
|
|
732
|
+
- Belongs to: Organization
|
|
733
|
+
- References: PolicyTemplate (defaults)
|
|
734
|
+
- Controls: PolicyBinding enforcement mode
|
|
735
|
+
- Evaluated by: Kyverno (when installed)
|
|
736
|
+
|
|
737
|
+
---
|
|
738
|
+
|
|
739
|
+
#### PolicyTemplate
|
|
740
|
+
|
|
741
|
+
| Field | Value |
|
|
742
|
+
|-------|-------|
|
|
743
|
+
| Storage | etcd |
|
|
744
|
+
| Context | policy |
|
|
745
|
+
| Plural | policytemplates |
|
|
746
|
+
| Namespace | org-scoped |
|
|
747
|
+
|
|
748
|
+
**Purpose:** Curated Kyverno policy template with parameters, rollout defaults,
|
|
749
|
+
target kinds, and remediation guidance.
|
|
750
|
+
|
|
751
|
+
**Required Spec Fields:**
|
|
752
|
+
- `displayName` — template name
|
|
753
|
+
- `targetKinds` — resource kinds this policy applies to
|
|
754
|
+
- `kyverno` — Kyverno policy definition (ClusterPolicy or Policy body)
|
|
755
|
+
|
|
756
|
+
**Optional Spec Fields:**
|
|
757
|
+
- `parameters` — configurable parameters with defaults
|
|
758
|
+
- `rolloutDefault` — default mode when bound (audit/enforce)
|
|
759
|
+
- `remediation` — guidance text for violations
|
|
760
|
+
- `severity` — low/medium/high/critical
|
|
761
|
+
|
|
762
|
+
**Behavior:**
|
|
763
|
+
- Templates are the library of available policies
|
|
764
|
+
- Binding a template creates the actual Kyverno policy resources
|
|
765
|
+
- Parameters are substituted into the Kyverno spec at binding time
|
|
766
|
+
|
|
767
|
+
**Relationships:**
|
|
768
|
+
- Referenced by: PolicyBinding, PolicyProfile
|
|
769
|
+
- Produces: Kyverno ClusterPolicy/Policy (when bound and Kyverno installed)
|
|
770
|
+
|
|
771
|
+
---
|
|
772
|
+
|
|
773
|
+
#### PolicyBinding
|
|
774
|
+
|
|
775
|
+
| Field | Value |
|
|
776
|
+
|-------|-------|
|
|
777
|
+
| Storage | etcd |
|
|
778
|
+
| Context | policy |
|
|
779
|
+
| Plural | policybindings |
|
|
780
|
+
| Namespace | org-scoped |
|
|
781
|
+
|
|
782
|
+
**Purpose:** Binding from a policy template to organization, repository, environment,
|
|
783
|
+
or resource selectors with audit/enforce rollout state.
|
|
784
|
+
|
|
785
|
+
**Required Spec Fields:**
|
|
786
|
+
- `organizationRef` — owning organization
|
|
787
|
+
- `templateRef` — reference to PolicyTemplate
|
|
788
|
+
- `mode` — current mode: `audit`, `enforce`, `disabled`
|
|
789
|
+
|
|
790
|
+
**Optional Spec Fields:**
|
|
791
|
+
- `scope` — `{ repositories[], environments[], labels }` targeting
|
|
792
|
+
- `parameters` — overrides for template parameters
|
|
793
|
+
- `exceptions[]` — PolicyExceptionRequest references
|
|
794
|
+
|
|
795
|
+
**Behavior:**
|
|
796
|
+
- When mode is `enforce` and Kyverno is installed:
|
|
797
|
+
- Generates a KyvernoPolicy or KyvernoClusterPolicy resource
|
|
798
|
+
- Policy violations block resource admission
|
|
799
|
+
- When mode is `audit`:
|
|
800
|
+
- Policy runs in audit mode; violations appear in PolicyReports
|
|
801
|
+
- When Kyverno is NOT installed:
|
|
802
|
+
- `requireForEnforceMode` env check; policies page shows info banner
|
|
803
|
+
|
|
804
|
+
**Relationships:**
|
|
805
|
+
- Belongs to: Organization
|
|
806
|
+
- References: PolicyTemplate
|
|
807
|
+
- Produces: Kyverno policy resources
|
|
808
|
+
- Has: PolicyExceptionRequest (exceptions to this binding)
|
|
809
|
+
|
|
810
|
+
---
|
|
811
|
+
|
|
812
|
+
#### PolicyExceptionRequest
|
|
813
|
+
|
|
814
|
+
| Field | Value |
|
|
815
|
+
|-------|-------|
|
|
816
|
+
| Storage | etcd |
|
|
817
|
+
| Context | policy |
|
|
818
|
+
| Plural | policyexceptionrequests |
|
|
819
|
+
| Namespace | org-scoped |
|
|
820
|
+
|
|
821
|
+
**Purpose:** Auditable request and approval workflow for temporary Kyverno
|
|
822
|
+
PolicyException resources. Allows teams to request time-limited exceptions to
|
|
823
|
+
enforced policies.
|
|
824
|
+
|
|
825
|
+
**Required Spec Fields:**
|
|
826
|
+
- `organizationRef` — owning organization
|
|
827
|
+
- `policyRef` — reference to the PolicyBinding being excepted
|
|
828
|
+
- `justification` — reason for the exception
|
|
829
|
+
- `expiresAt` — ISO 8601 expiration timestamp
|
|
830
|
+
|
|
831
|
+
**Lifecycle:**
|
|
832
|
+
- `Pending` — request submitted, awaiting approval
|
|
833
|
+
- `Approved` — approved; KyvernoPolicyException created
|
|
834
|
+
- `Denied` — rejected by policy admin
|
|
835
|
+
- `Expired` — past expiresAt; exception removed
|
|
836
|
+
- `Revoked` — manually cancelled
|
|
837
|
+
|
|
838
|
+
**Behavior:**
|
|
839
|
+
- On approval: creates `KyvernoPolicyException` resource in policy namespace
|
|
840
|
+
- On expiry/revocation: deletes the KyvernoPolicyException
|
|
841
|
+
- Full audit trail maintained in resource history
|
|
842
|
+
|
|
843
|
+
**Relationships:**
|
|
844
|
+
- Belongs to: Organization
|
|
845
|
+
- References: PolicyBinding (the excepted policy)
|
|
846
|
+
- Produces: KyvernoPolicyException (when approved and Kyverno installed)
|
|
847
|
+
|
|
848
|
+
---
|
|
849
|
+
|
|
850
|
+
### 1.4 Web UI Domain (2 kinds)
|
|
851
|
+
|
|
852
|
+
---
|
|
853
|
+
|
|
854
|
+
#### View
|
|
855
|
+
|
|
856
|
+
| Field | Value |
|
|
857
|
+
|-------|-------|
|
|
858
|
+
| Storage | etcd |
|
|
859
|
+
| Context | web-ui |
|
|
860
|
+
| Plural | views |
|
|
861
|
+
| Namespace | org-scoped |
|
|
862
|
+
|
|
863
|
+
**Purpose:** Saved triage and dashboard view backed by resource selectors. Allows users
|
|
864
|
+
to create custom filtered views of resources.
|
|
865
|
+
|
|
866
|
+
**Required Spec Fields:**
|
|
867
|
+
- `organizationRef` — owning organization
|
|
868
|
+
- `selector` — reference to a Selector resource or inline selector spec
|
|
869
|
+
|
|
870
|
+
**Optional Spec Fields:**
|
|
871
|
+
- `columns[]` — column definitions for table display
|
|
872
|
+
- `sort[]` — sort order specifications
|
|
873
|
+
|
|
874
|
+
**Relationships:**
|
|
875
|
+
- Belongs to: Organization
|
|
876
|
+
- References: Selector (filter definition)
|
|
877
|
+
- Displayed by: web console dashboard
|
|
878
|
+
|
|
879
|
+
---
|
|
880
|
+
|
|
881
|
+
#### Selector
|
|
882
|
+
|
|
883
|
+
| Field | Value |
|
|
884
|
+
|-------|-------|
|
|
885
|
+
| Storage | etcd |
|
|
886
|
+
| Context | web-ui |
|
|
887
|
+
| Plural | selectors |
|
|
888
|
+
| Namespace | org-scoped |
|
|
889
|
+
|
|
890
|
+
**Purpose:** Reusable label/query selector for workflows and views. Encapsulates filter
|
|
891
|
+
criteria that can be shared across multiple Views.
|
|
892
|
+
|
|
893
|
+
**Required Spec Fields:**
|
|
894
|
+
- `organizationRef` — owning organization
|
|
895
|
+
|
|
896
|
+
**Optional Spec Fields:**
|
|
897
|
+
- `labels` — label selector map
|
|
898
|
+
- `query` — free-text query string
|
|
899
|
+
|
|
900
|
+
**Helper:** `createSelector()` factory function in resource-model.js
|
|
901
|
+
|
|
902
|
+
**Relationships:**
|
|
903
|
+
- Belongs to: Organization
|
|
904
|
+
- Referenced by: View resources
|
|
905
|
+
|
|
906
|
+
---
|
|
907
|
+
|
|
908
|
+
### 1.5 Agent Domain (28 kinds)
|
|
909
|
+
|
|
910
|
+
The agent domain is the largest, comprising resources for AI agent orchestration,
|
|
911
|
+
dispatch, sessions, tools, providers, memory, workspaces, projects, and approval gates.
|
|
912
|
+
|
|
913
|
+
---
|
|
914
|
+
|
|
915
|
+
#### AgentStack
|
|
916
|
+
|
|
917
|
+
| Field | Value |
|
|
918
|
+
|-------|-------|
|
|
919
|
+
| Storage | etcd |
|
|
920
|
+
| Context | agents |
|
|
921
|
+
| Plural | agentstacks |
|
|
922
|
+
| Namespace | org-scoped |
|
|
923
|
+
|
|
924
|
+
**Purpose:** Reusable agent definition — the "recipe" for an agent. Specifies the base
|
|
925
|
+
agent, adapter, provider, model, prompt templates, MCP server references, skill
|
|
926
|
+
references, subagent references, context labels, approval mode, and runner policy.
|
|
927
|
+
|
|
928
|
+
**Required Spec Fields:**
|
|
929
|
+
- `organizationRef` — owning organization
|
|
930
|
+
- `baseAgent` — agent type identifier
|
|
931
|
+
- `adapter` — adapter reference (e.g., 'claude-code', 'openai')
|
|
932
|
+
- `runtimeIdentity` — AgentServiceAccount reference or inline config
|
|
933
|
+
|
|
934
|
+
**Optional Spec Fields:**
|
|
935
|
+
- `provider` — model provider (e.g., 'anthropic', 'openai')
|
|
936
|
+
- `model` — model identifier
|
|
937
|
+
- `promptTemplates` — system/user prompt fragments
|
|
938
|
+
- `toolPolicy` / `toolPolicyRef` — reference to AgentToolProfile
|
|
939
|
+
- `mcpServerRefs[]` — references to AgentMcpServer resources
|
|
940
|
+
- `skillRefs[]` — references to AgentSkill resources
|
|
941
|
+
- `subagentRefs[]` — references to AgentSubagent resources
|
|
942
|
+
- `contextLabelRefs[]` — references to AgentContextLabel resources
|
|
943
|
+
- `approvalMode` — when to require human approval
|
|
944
|
+
- `workspacePolicy` — reference to KradleWorkspacePolicy
|
|
945
|
+
- `taskKind` — default task kind
|
|
946
|
+
|
|
947
|
+
**Stack Reconciliation (`reconcileStack()`):**
|
|
948
|
+
Produces conditions indicating readiness:
|
|
949
|
+
1. `CapabilitiesResolved` — all ref fields resolve to existing resources
|
|
950
|
+
2. `ToolsAdmitted` — AgentToolProfile found (if referenced)
|
|
951
|
+
3. `McpHealthy` — all referenced MCP servers exist
|
|
952
|
+
4. `SkillsValidated` — all skills have valid format and sourceRef
|
|
953
|
+
5. `SubagentsValid` — all subagents have non-empty taskKinds
|
|
954
|
+
6. `ContextLabelsValid` — all context labels exist
|
|
955
|
+
7. `RuntimeIdentityReady` — AgentServiceAccount exists
|
|
956
|
+
8. `RolesAdmitted` — AgentRoleBinding requirements met
|
|
957
|
+
9. `SecretsAdmitted` — AgentSecretGrant requirements met
|
|
958
|
+
10. `ConfigAdmitted` — AgentConfigGrant requirements met
|
|
959
|
+
11. `Ready` — all above conditions are True
|
|
960
|
+
|
|
961
|
+
**MCP Health Check (`checkMcpHealth()`):**
|
|
962
|
+
- HTTP GET to MCP server endpoint with 3-second timeout
|
|
963
|
+
- Returns: `{ serverName, status: 'healthy'|'unhealthy'|'unknown', latencyMs }`
|
|
964
|
+
|
|
965
|
+
**Capabilities List (`listStackCapabilities()`):**
|
|
966
|
+
- Returns array of `{ kind, name, status, ref }` for all referenced capabilities
|
|
967
|
+
|
|
968
|
+
**Relationships:**
|
|
969
|
+
- Belongs to: Organization
|
|
970
|
+
- References: AgentSubagent, AgentToolProfile, AgentMcpServer, AgentSkill,
|
|
971
|
+
AgentContextLabel, KradleWorkspacePolicy, AgentServiceAccount
|
|
972
|
+
- Referenced by: AgentDispatchRun, AgentTriggerRule, KradleProject
|
|
973
|
+
- Controls: which tools, prompts, and resources an agent session has access to
|
|
974
|
+
|
|
975
|
+
---
|
|
976
|
+
|
|
977
|
+
#### AgentSubagent
|
|
978
|
+
|
|
979
|
+
| Field | Value |
|
|
980
|
+
|-------|-------|
|
|
981
|
+
| Storage | etcd |
|
|
982
|
+
| Context | agents |
|
|
983
|
+
| Plural | agentsubagents |
|
|
984
|
+
| Namespace | org-scoped |
|
|
985
|
+
|
|
986
|
+
**Purpose:** Named child-agent definition with role prompt, task kinds, tool subset,
|
|
987
|
+
and workspace scope. Enables hierarchical agent composition where a parent stack
|
|
988
|
+
can delegate to specialized subagents.
|
|
989
|
+
|
|
990
|
+
**Required Spec Fields:**
|
|
991
|
+
- `organizationRef` — owning organization
|
|
992
|
+
- `rolePrompt` — system prompt defining the subagent's role
|
|
993
|
+
- `taskKinds` — array of task kinds this subagent handles
|
|
994
|
+
|
|
995
|
+
**Optional Spec Fields:**
|
|
996
|
+
- `toolSubset[]` — restricted tool list (subset of parent)
|
|
997
|
+
- `workspaceScope` — workspace access level
|
|
998
|
+
- `maxConcurrency` — max parallel executions
|
|
999
|
+
|
|
1000
|
+
**Validation:**
|
|
1001
|
+
- `taskKinds` must be a non-empty array (checked during stack reconciliation)
|
|
1002
|
+
|
|
1003
|
+
**Relationships:**
|
|
1004
|
+
- Belongs to: Organization
|
|
1005
|
+
- Referenced by: AgentStack (via subagentRefs)
|
|
1006
|
+
- Used during: dispatch to determine which subagent handles a task
|
|
1007
|
+
|
|
1008
|
+
---
|
|
1009
|
+
|
|
1010
|
+
#### AgentToolProfile
|
|
1011
|
+
|
|
1012
|
+
| Field | Value |
|
|
1013
|
+
|-------|-------|
|
|
1014
|
+
| Storage | etcd |
|
|
1015
|
+
| Context | agents |
|
|
1016
|
+
| Plural | agenttoolprofiles |
|
|
1017
|
+
| Namespace | org-scoped |
|
|
1018
|
+
|
|
1019
|
+
**Purpose:** Native tool policy defining filesystem access, network access, shell
|
|
1020
|
+
access, and per-tool approval gates. Controls what an agent is allowed to do.
|
|
1021
|
+
|
|
1022
|
+
**Required Spec Fields:**
|
|
1023
|
+
- `organizationRef` — owning organization
|
|
1024
|
+
- `filesystemPolicy` — filesystem access rules
|
|
1025
|
+
- `approvalPolicyByTool` — map of tool names to approval requirements
|
|
1026
|
+
|
|
1027
|
+
**Optional Spec Fields:**
|
|
1028
|
+
- `networkPolicy` — network access rules
|
|
1029
|
+
- `shellPolicy` — shell command execution rules
|
|
1030
|
+
- `denyList[]` — explicitly denied tool/operation patterns
|
|
1031
|
+
|
|
1032
|
+
**Relationships:**
|
|
1033
|
+
- Belongs to: Organization
|
|
1034
|
+
- Referenced by: AgentStack (via toolPolicy/toolPolicyRef)
|
|
1035
|
+
- Evaluated during: agent execution for tool-use gating
|
|
1036
|
+
|
|
1037
|
+
---
|
|
1038
|
+
|
|
1039
|
+
#### AgentMcpServer
|
|
1040
|
+
|
|
1041
|
+
| Field | Value |
|
|
1042
|
+
|-------|-------|
|
|
1043
|
+
| Storage | etcd |
|
|
1044
|
+
| Context | agents |
|
|
1045
|
+
| Plural | agentmcpservers |
|
|
1046
|
+
| Namespace | org-scoped |
|
|
1047
|
+
|
|
1048
|
+
**Purpose:** Managed MCP (Model Context Protocol) endpoint registration with transport
|
|
1049
|
+
type, discovery metadata, health check configuration, and secret/config references.
|
|
1050
|
+
|
|
1051
|
+
**Required Spec Fields:**
|
|
1052
|
+
- `organizationRef` — owning organization
|
|
1053
|
+
- `transport` — transport type (e.g., 'stdio', 'http', 'sse')
|
|
1054
|
+
- `scope` — access scope
|
|
1055
|
+
|
|
1056
|
+
**Optional Spec Fields:**
|
|
1057
|
+
- `endpoint` — HTTP endpoint URL (for health checks)
|
|
1058
|
+
- `discoveryUrl` — MCP discovery URL
|
|
1059
|
+
- `secretRefs[]` — references to Kubernetes Secrets
|
|
1060
|
+
- `configRefs[]` — references to ConfigMaps
|
|
1061
|
+
- `healthCheck` — health check configuration
|
|
1062
|
+
|
|
1063
|
+
**Health Monitoring:**
|
|
1064
|
+
- `checkMcpHealth()` in stack controller performs HTTP GET with 3s timeout
|
|
1065
|
+
- Status: `healthy` (response.ok), `unhealthy` (error/timeout), `unknown` (no endpoint)
|
|
1066
|
+
|
|
1067
|
+
**Relationships:**
|
|
1068
|
+
- Belongs to: Organization
|
|
1069
|
+
- Referenced by: AgentStack (via mcpServerRefs)
|
|
1070
|
+
- Provides: tools and resources to agent sessions via MCP protocol
|
|
1071
|
+
|
|
1072
|
+
---
|
|
1073
|
+
|
|
1074
|
+
#### AgentSkill
|
|
1075
|
+
|
|
1076
|
+
| Field | Value |
|
|
1077
|
+
|-------|-------|
|
|
1078
|
+
| Storage | etcd |
|
|
1079
|
+
| Context | agents |
|
|
1080
|
+
| Plural | agentskills |
|
|
1081
|
+
| Namespace | org-scoped |
|
|
1082
|
+
|
|
1083
|
+
**Purpose:** Reusable runbook/procedure bundle with prompt fragments, tool dependencies,
|
|
1084
|
+
and output contracts. Skills encapsulate specialized knowledge and procedures.
|
|
1085
|
+
|
|
1086
|
+
**Required Spec Fields:**
|
|
1087
|
+
- `organizationRef` — owning organization
|
|
1088
|
+
- `format` — skill format (e.g., 'markdown', 'yaml')
|
|
1089
|
+
- `sourceRef` — reference to the skill source (git path, URL, etc.)
|
|
1090
|
+
|
|
1091
|
+
**Optional Spec Fields:**
|
|
1092
|
+
- `promptFragments[]` — prompt fragments contributed by this skill
|
|
1093
|
+
- `toolDeps[]` — required tools
|
|
1094
|
+
- `outputContract` — expected output schema
|
|
1095
|
+
|
|
1096
|
+
**Validation:**
|
|
1097
|
+
- Must have non-empty `format` and `sourceRef` (checked during stack reconciliation)
|
|
1098
|
+
|
|
1099
|
+
**Relationships:**
|
|
1100
|
+
- Belongs to: Organization
|
|
1101
|
+
- Referenced by: AgentStack (via skillRefs)
|
|
1102
|
+
- Injected into: agent context during session initialization
|
|
1103
|
+
|
|
1104
|
+
---
|
|
1105
|
+
|
|
1106
|
+
#### AgentTriggerRule
|
|
1107
|
+
|
|
1108
|
+
| Field | Value |
|
|
1109
|
+
|-------|-------|
|
|
1110
|
+
| Storage | etcd |
|
|
1111
|
+
| Context | agents |
|
|
1112
|
+
| Plural | agenttriggerrules |
|
|
1113
|
+
| Namespace | org-scoped |
|
|
1114
|
+
|
|
1115
|
+
**Purpose:** Event-to-stack routing rules for CI failures, webhooks, comments, labels,
|
|
1116
|
+
schedules (cron), and manual dispatch. Determines which events should trigger which
|
|
1117
|
+
agent stacks.
|
|
1118
|
+
|
|
1119
|
+
**Required Spec Fields:**
|
|
1120
|
+
- `organizationRef` — owning organization
|
|
1121
|
+
- `sources` — event source types (e.g., ['push', 'pull_request', 'issue_comment'])
|
|
1122
|
+
- `agentStack` — reference to AgentStack to dispatch
|
|
1123
|
+
- `taskKind` — task kind for the dispatch
|
|
1124
|
+
|
|
1125
|
+
**Optional Spec Fields:**
|
|
1126
|
+
- `enabled` — boolean toggle (default: true)
|
|
1127
|
+
- `repository` — scope to specific repository
|
|
1128
|
+
- `allowedActors[]` — restrict triggering to specific users
|
|
1129
|
+
- `cronExpression` — 5-field cron for scheduled triggers
|
|
1130
|
+
- `webhookTrigger` — `{ url, events[], repository, action, secretRef }`
|
|
1131
|
+
- `commentTrigger` — `{ pattern, repos[] }`
|
|
1132
|
+
- `labelTrigger` — `{ labels[], action: 'labeled'|'unlabeled' }`
|
|
1133
|
+
|
|
1134
|
+
**Source Type Detection (`getTriggerSourceType()`):**
|
|
1135
|
+
- `cronExpression` present → `cron`
|
|
1136
|
+
- `webhookTrigger` present → `webhook`
|
|
1137
|
+
- `commentTrigger` present → `comment`
|
|
1138
|
+
- `labelTrigger` present → `label`
|
|
1139
|
+
- `sources` present → `event`
|
|
1140
|
+
- None → `unknown`
|
|
1141
|
+
|
|
1142
|
+
**Validation (`validateTriggerRule()`):**
|
|
1143
|
+
- Validates source-specific sub-configs (cron expression, webhook URL, etc.)
|
|
1144
|
+
- Cron: 5 fields, valid characters only
|
|
1145
|
+
- Webhook: valid HTTP/HTTPS URL
|
|
1146
|
+
- Comment: non-empty pattern string
|
|
1147
|
+
- Label: non-empty labels array, valid action
|
|
1148
|
+
|
|
1149
|
+
**Cron Scheduling:**
|
|
1150
|
+
- `validateCronExpression()` — validates 5-field cron syntax
|
|
1151
|
+
- `calculateNextRun()` — computes next execution time (minute precision)
|
|
1152
|
+
|
|
1153
|
+
**Event Evaluation (`evaluateEvent()`):**
|
|
1154
|
+
- Matches event type against rule sources
|
|
1155
|
+
- Checks repository scope filter
|
|
1156
|
+
- Checks actor filter (allowedActors)
|
|
1157
|
+
- Deduplication: skips if identical execution already exists (non-Failed)
|
|
1158
|
+
|
|
1159
|
+
**Webhook Event Evaluation (`evaluateWebhookEvent()`):**
|
|
1160
|
+
- Checks enabled flag
|
|
1161
|
+
- Matches eventType against webhookTrigger.events (or wildcard '*')
|
|
1162
|
+
- Filters by webhookTrigger.repository
|
|
1163
|
+
- Filters by webhookTrigger.action
|
|
1164
|
+
- Deduplicates by rule name
|
|
1165
|
+
- Returns matchingRules + dispatchIntents
|
|
1166
|
+
|
|
1167
|
+
**Relationships:**
|
|
1168
|
+
- Belongs to: Organization
|
|
1169
|
+
- References: AgentStack (dispatch target)
|
|
1170
|
+
- Produces: AgentTriggerExecution (evaluation record), AgentDispatchRun (on match)
|
|
1171
|
+
- Consumed by: webhook controller, event bus, cron scheduler
|
|
1172
|
+
|
|
1173
|
+
---
|
|
1174
|
+
|
|
1175
|
+
#### AgentContextLabel
|
|
1176
|
+
|
|
1177
|
+
| Field | Value |
|
|
1178
|
+
|-------|-------|
|
|
1179
|
+
| Storage | etcd |
|
|
1180
|
+
| Context | agents |
|
|
1181
|
+
| Plural | agentcontextlabels |
|
|
1182
|
+
| Namespace | org-scoped |
|
|
1183
|
+
|
|
1184
|
+
**Purpose:** Reviewed prompt fragment with provenance tracking and allowlisted sources.
|
|
1185
|
+
Provides curated context that can be injected into agent sessions.
|
|
1186
|
+
|
|
1187
|
+
**Required Spec Fields:**
|
|
1188
|
+
- `organizationRef` — owning organization
|
|
1189
|
+
- `promptFragment` — the prompt text to inject
|
|
1190
|
+
- `allowedSources` — which stacks/contexts may use this label
|
|
1191
|
+
|
|
1192
|
+
**Relationships:**
|
|
1193
|
+
- Belongs to: Organization
|
|
1194
|
+
- Referenced by: AgentStack (via contextLabelRefs)
|
|
1195
|
+
- Injected into: AgentContextBundle during dispatch
|
|
1196
|
+
|
|
1197
|
+
---
|
|
1198
|
+
|
|
1199
|
+
#### KradleWorkspacePolicy
|
|
1200
|
+
|
|
1201
|
+
| Field | Value |
|
|
1202
|
+
|-------|-------|
|
|
1203
|
+
| Storage | etcd |
|
|
1204
|
+
| Context | agents |
|
|
1205
|
+
| Plural | kradleworkspacepolicies |
|
|
1206
|
+
| Namespace | org-scoped |
|
|
1207
|
+
|
|
1208
|
+
**Purpose:** Git worktree provisioning, cleanup, retention, and trust tier policies.
|
|
1209
|
+
Controls how agent workspaces are created, reused, and cleaned up.
|
|
1210
|
+
|
|
1211
|
+
**Required Spec Fields:**
|
|
1212
|
+
- `organizationRef` — owning organization
|
|
1213
|
+
- `mode` — provisioning mode (e.g., 'on-demand', 'pre-warmed')
|
|
1214
|
+
- `retentionPolicy` — `{ maxAge, maxCount, cleanupSchedule }`
|
|
1215
|
+
|
|
1216
|
+
**Optional Spec Fields:**
|
|
1217
|
+
- `trustTier` — workspace trust level
|
|
1218
|
+
- `storageClassName` — PVC storage class
|
|
1219
|
+
- `defaultCapacity` — default PVC size
|
|
1220
|
+
|
|
1221
|
+
**Relationships:**
|
|
1222
|
+
- Belongs to: Organization
|
|
1223
|
+
- Referenced by: AgentStack (workspace policy)
|
|
1224
|
+
- Controls: KradleWorkspace creation and lifecycle
|
|
1225
|
+
|
|
1226
|
+
---
|
|
1227
|
+
|
|
1228
|
+
#### AgentAdapter
|
|
1229
|
+
|
|
1230
|
+
| Field | Value |
|
|
1231
|
+
|-------|-------|
|
|
1232
|
+
| Storage | etcd |
|
|
1233
|
+
| Context | agents |
|
|
1234
|
+
| Plural | agentadapters |
|
|
1235
|
+
| Namespace | org-scoped |
|
|
1236
|
+
|
|
1237
|
+
**Purpose:** Agent adapter definition declaring transport type, capabilities matrix,
|
|
1238
|
+
authentication requirements, and installation method. Adapters are the bridge between
|
|
1239
|
+
the Kradle control plane and actual agent runtimes (e.g., Claude Code, OpenAI, etc.).
|
|
1240
|
+
|
|
1241
|
+
**Required Spec Fields:**
|
|
1242
|
+
- `organizationRef` — owning organization
|
|
1243
|
+
- `adapterType` — adapter identifier (e.g., 'claude-code', 'openai-assistants')
|
|
1244
|
+
- `transport` — communication transport (e.g., 'http', 'stdio', 'websocket')
|
|
1245
|
+
|
|
1246
|
+
**Optional Spec Fields:**
|
|
1247
|
+
- `capabilities[]` — supported operations/features
|
|
1248
|
+
- `authRequirements` — what credentials the adapter needs
|
|
1249
|
+
- `installationMethod` — how to deploy the adapter
|
|
1250
|
+
|
|
1251
|
+
**Relationships:**
|
|
1252
|
+
- Belongs to: Organization
|
|
1253
|
+
- Referenced by: AgentTransportBinding (connection config)
|
|
1254
|
+
- Used by: AgentStack (via adapter field)
|
|
1255
|
+
|
|
1256
|
+
---
|
|
1257
|
+
|
|
1258
|
+
#### AgentTransportBinding
|
|
1259
|
+
|
|
1260
|
+
| Field | Value |
|
|
1261
|
+
|-------|-------|
|
|
1262
|
+
| Storage | etcd |
|
|
1263
|
+
| Context | agents |
|
|
1264
|
+
| Plural | agenttransportbindings |
|
|
1265
|
+
| Namespace | org-scoped |
|
|
1266
|
+
|
|
1267
|
+
**Purpose:** Connection configuration for an adapter instance with endpoint, protocol
|
|
1268
|
+
details, authentication, health check, and reconnect policy.
|
|
1269
|
+
|
|
1270
|
+
**Required Spec Fields:**
|
|
1271
|
+
- `organizationRef` — owning organization
|
|
1272
|
+
- `adapterRef` — reference to AgentAdapter
|
|
1273
|
+
- `endpoint` — connection endpoint URL
|
|
1274
|
+
- `protocol` — protocol version/details
|
|
1275
|
+
|
|
1276
|
+
**Optional Spec Fields:**
|
|
1277
|
+
- `auth` — authentication configuration (token refs, etc.)
|
|
1278
|
+
- `healthCheck` — `{ interval, timeout, path }`
|
|
1279
|
+
- `reconnectPolicy` — `{ maxRetries, backoffMs }`
|
|
1280
|
+
- `tls` — TLS configuration
|
|
1281
|
+
|
|
1282
|
+
**Relationships:**
|
|
1283
|
+
- Belongs to: Organization
|
|
1284
|
+
- References: AgentAdapter
|
|
1285
|
+
- Used by: Agent Mux client for session establishment
|
|
1286
|
+
|
|
1287
|
+
---
|
|
1288
|
+
|
|
1289
|
+
#### AgentProviderConfig
|
|
1290
|
+
|
|
1291
|
+
| Field | Value |
|
|
1292
|
+
|-------|-------|
|
|
1293
|
+
| Storage | etcd |
|
|
1294
|
+
| Context | agents |
|
|
1295
|
+
| Plural | agentproviderconfigs |
|
|
1296
|
+
| Namespace | org-scoped |
|
|
1297
|
+
|
|
1298
|
+
**Purpose:** Model provider configuration with API base URL, authentication type,
|
|
1299
|
+
default model, model translation mappings, and rate limits.
|
|
1300
|
+
|
|
1301
|
+
**Required Spec Fields:**
|
|
1302
|
+
- `organizationRef` — owning organization
|
|
1303
|
+
- `provider` — provider identifier (e.g., 'anthropic', 'openai')
|
|
1304
|
+
- `authType` — authentication method (e.g., 'api-key', 'oauth')
|
|
1305
|
+
|
|
1306
|
+
**Optional Spec Fields:**
|
|
1307
|
+
- `apiBase` — API base URL override
|
|
1308
|
+
- `defaultModel` — default model to use
|
|
1309
|
+
- `modelTranslations` — map of model aliases to actual model IDs
|
|
1310
|
+
- `rateLimits` — `{ requestsPerMinute, tokensPerMinute }`
|
|
1311
|
+
- `secretRef` — reference to K8s Secret containing API key
|
|
1312
|
+
|
|
1313
|
+
**Relationships:**
|
|
1314
|
+
- Belongs to: Organization
|
|
1315
|
+
- Referenced by: AgentStack (provider configuration)
|
|
1316
|
+
- Used by: Agent Mux for model API calls
|
|
1317
|
+
|
|
1318
|
+
---
|
|
1319
|
+
|
|
1320
|
+
#### AgentGatewayConfig
|
|
1321
|
+
|
|
1322
|
+
| Field | Value |
|
|
1323
|
+
|-------|-------|
|
|
1324
|
+
| Storage | etcd |
|
|
1325
|
+
| Context | agents |
|
|
1326
|
+
| Plural | agentgatewayconfigs |
|
|
1327
|
+
| Namespace | org-scoped |
|
|
1328
|
+
|
|
1329
|
+
**Purpose:** Runtime Agent Mux gateway connection settings with URL, auth configuration,
|
|
1330
|
+
reconnect policy, and feature flags.
|
|
1331
|
+
|
|
1332
|
+
**Required Spec Fields:**
|
|
1333
|
+
- `organizationRef` — owning organization
|
|
1334
|
+
- `gatewayUrl` — Agent Mux gateway URL
|
|
1335
|
+
|
|
1336
|
+
**Optional Spec Fields:**
|
|
1337
|
+
- `auth` — gateway authentication config
|
|
1338
|
+
- `reconnectPolicy` — `{ maxRetries, backoffMs }`
|
|
1339
|
+
- `featureFlags` — feature toggles for the gateway
|
|
1340
|
+
- `sseEnabled` — enable SSE event streaming
|
|
1341
|
+
|
|
1342
|
+
**Relationships:**
|
|
1343
|
+
- Belongs to: Organization
|
|
1344
|
+
- Used by: Agent Mux client (`createAgentMuxClient()`)
|
|
1345
|
+
- Controls: how the dispatch controller connects to the agent runtime
|
|
1346
|
+
|
|
1347
|
+
---
|
|
1348
|
+
|
|
1349
|
+
#### KradleProject
|
|
1350
|
+
|
|
1351
|
+
| Field | Value |
|
|
1352
|
+
|-------|-------|
|
|
1353
|
+
| Storage | etcd |
|
|
1354
|
+
| Context | agents |
|
|
1355
|
+
| Plural | kradleprojects |
|
|
1356
|
+
| Namespace | org-scoped |
|
|
1357
|
+
|
|
1358
|
+
**Purpose:** Organization project grouping issues, linked repositories, kanban board
|
|
1359
|
+
configuration, default workflow, and backend sync references.
|
|
1360
|
+
|
|
1361
|
+
**Required Spec Fields:**
|
|
1362
|
+
- `organizationRef` — owning organization
|
|
1363
|
+
- `displayName` — project name
|
|
1364
|
+
|
|
1365
|
+
**Optional Spec Fields:**
|
|
1366
|
+
- `workflowColumns[]` — kanban column definitions: `[{ id, displayName, color, default? }]`
|
|
1367
|
+
- `boardState` — `active` or `archived`
|
|
1368
|
+
- `repositoryRefs[]` — linked repositories
|
|
1369
|
+
- `stackRefs[]` — linked agent stacks
|
|
1370
|
+
- `syncRefs[]` — external project sync references (GitHub Projects, etc.)
|
|
1371
|
+
|
|
1372
|
+
**Validation (`validateAgentProject()`):**
|
|
1373
|
+
- metadata.name required
|
|
1374
|
+
- organizationRef required
|
|
1375
|
+
- workflowColumns: non-empty array, no duplicate column IDs
|
|
1376
|
+
- boardState: must be `active` or `archived` (if set)
|
|
1377
|
+
|
|
1378
|
+
**Board Operations:**
|
|
1379
|
+
- `getWorkflowColumns()` — returns ordered column array
|
|
1380
|
+
- `getDefaultColumn()` — column with `default: true`, or first column
|
|
1381
|
+
- `getBoardState()` — returns current board state (default: 'active')
|
|
1382
|
+
|
|
1383
|
+
**Issue Assignment:**
|
|
1384
|
+
- Issues reference projects via `spec.projectRefs`
|
|
1385
|
+
- Issues have `workflowState` field corresponding to column IDs
|
|
1386
|
+
- Kanban drag-drop updates issue workflowState
|
|
1387
|
+
|
|
1388
|
+
**Relationships:**
|
|
1389
|
+
- Belongs to: Organization
|
|
1390
|
+
- Groups: Issues (via projectRefs on issues)
|
|
1391
|
+
- References: Repositories (repositoryRefs), AgentStacks (stackRefs)
|
|
1392
|
+
- Synced to: GitHub Projects (via ExternalBackendBinding)
|
|
1393
|
+
|
|
1394
|
+
---
|
|
1395
|
+
|
|
1396
|
+
#### AgentRoleBinding
|
|
1397
|
+
|
|
1398
|
+
| Field | Value |
|
|
1399
|
+
|-------|-------|
|
|
1400
|
+
| Storage | etcd |
|
|
1401
|
+
| Context | identity |
|
|
1402
|
+
| Plural | agentrolebindings |
|
|
1403
|
+
| Namespace | org-scoped |
|
|
1404
|
+
|
|
1405
|
+
**Purpose:** Managed projection to native Kubernetes RBAC for agent identity. Maps
|
|
1406
|
+
agent service accounts to cluster roles and namespaced roles.
|
|
1407
|
+
|
|
1408
|
+
**Required Spec Fields:**
|
|
1409
|
+
- `organizationRef` — owning organization
|
|
1410
|
+
- `subject` — AgentServiceAccount or user reference
|
|
1411
|
+
- `roleRef` — Kubernetes Role or ClusterRole reference
|
|
1412
|
+
- `scope` — namespace or cluster scope
|
|
1413
|
+
|
|
1414
|
+
**Relationships:**
|
|
1415
|
+
- Belongs to: Organization
|
|
1416
|
+
- References: AgentServiceAccount (subject)
|
|
1417
|
+
- Produces: Kubernetes RoleBinding/ClusterRoleBinding
|
|
1418
|
+
- Checked by: stack reconciliation (RolesAdmitted condition)
|
|
1419
|
+
|
|
1420
|
+
---
|
|
1421
|
+
|
|
1422
|
+
#### AgentSecretGrant
|
|
1423
|
+
|
|
1424
|
+
| Field | Value |
|
|
1425
|
+
|-------|-------|
|
|
1426
|
+
| Storage | etcd |
|
|
1427
|
+
| Context | identity |
|
|
1428
|
+
| Plural | agentsecretgrants |
|
|
1429
|
+
| Namespace | org-scoped |
|
|
1430
|
+
|
|
1431
|
+
**Purpose:** Explicit permission for a subject to access specific Secret keys with
|
|
1432
|
+
purpose scoping. Provides fine-grained secret access control for agents.
|
|
1433
|
+
|
|
1434
|
+
**Required Spec Fields:**
|
|
1435
|
+
- `organizationRef` — owning organization
|
|
1436
|
+
- `subject` — who gets access (AgentServiceAccount ref)
|
|
1437
|
+
- `secretRef` — K8s Secret reference `{ name, namespace, keys[] }`
|
|
1438
|
+
- `purpose` — why access is needed
|
|
1439
|
+
|
|
1440
|
+
**Relationships:**
|
|
1441
|
+
- Belongs to: Organization
|
|
1442
|
+
- Grants access to: Kubernetes Secrets
|
|
1443
|
+
- Checked by: stack reconciliation (SecretsAdmitted condition)
|
|
1444
|
+
- Checked by: permission reviewer during dispatch
|
|
1445
|
+
|
|
1446
|
+
---
|
|
1447
|
+
|
|
1448
|
+
#### AgentConfigGrant
|
|
1449
|
+
|
|
1450
|
+
| Field | Value |
|
|
1451
|
+
|-------|-------|
|
|
1452
|
+
| Storage | etcd |
|
|
1453
|
+
| Context | identity |
|
|
1454
|
+
| Plural | agentconfiggrants |
|
|
1455
|
+
| Namespace | org-scoped |
|
|
1456
|
+
|
|
1457
|
+
**Purpose:** Explicit permission for a subject to access specific ConfigMap keys with
|
|
1458
|
+
purpose scoping.
|
|
1459
|
+
|
|
1460
|
+
**Required Spec Fields:**
|
|
1461
|
+
- `organizationRef` — owning organization
|
|
1462
|
+
- `subject` — who gets access
|
|
1463
|
+
- `configMapRef` — K8s ConfigMap reference `{ name, namespace, keys[] }`
|
|
1464
|
+
- `purpose` — why access is needed
|
|
1465
|
+
|
|
1466
|
+
**Relationships:**
|
|
1467
|
+
- Belongs to: Organization
|
|
1468
|
+
- Grants access to: Kubernetes ConfigMaps
|
|
1469
|
+
- Checked by: stack reconciliation (ConfigAdmitted condition)
|
|
1470
|
+
|
|
1471
|
+
---
|
|
1472
|
+
|
|
1473
|
+
#### AgentDispatchRun
|
|
1474
|
+
|
|
1475
|
+
| Field | Value |
|
|
1476
|
+
|-------|-------|
|
|
1477
|
+
| Storage | postgres |
|
|
1478
|
+
| Context | agents |
|
|
1479
|
+
| Plural | agentdispatchruns |
|
|
1480
|
+
| Namespace | org-scoped |
|
|
1481
|
+
|
|
1482
|
+
**Purpose:** Logical CI-like run record visible alongside Pipeline/Job records. Represents
|
|
1483
|
+
a complete agent invocation with queue state, execution status, workspace binding,
|
|
1484
|
+
cost tracking, and memory snapshot reference.
|
|
1485
|
+
|
|
1486
|
+
**Required Spec Fields:**
|
|
1487
|
+
- `organizationRef` — owning organization
|
|
1488
|
+
- `repository` — target repository
|
|
1489
|
+
- `sourceRefs` — source references that triggered this run
|
|
1490
|
+
- `agentStack` — reference to AgentStack used
|
|
1491
|
+
- `taskKind` — the type of task (e.g., 'diagnostic', 'fix', 'review')
|
|
1492
|
+
|
|
1493
|
+
**Optional Spec Fields:**
|
|
1494
|
+
- `contextBundleRef` — reference to AgentContextBundle
|
|
1495
|
+
- `memorySnapshotRef` — reference to AgentMemorySnapshot
|
|
1496
|
+
- `workspaceRef` — reference to KradleWorkspace
|
|
1497
|
+
- `mountSpec` — volume/volumeMount configuration
|
|
1498
|
+
|
|
1499
|
+
**Lifecycle Phases:**
|
|
1500
|
+
- `Pending` — created, workspace being provisioned
|
|
1501
|
+
- `AwaitingApproval` — permission review requires approval
|
|
1502
|
+
- `Queued` — ready but Agent Mux not available
|
|
1503
|
+
- `Running` — session launched, executing
|
|
1504
|
+
- `Succeeded` — completed successfully
|
|
1505
|
+
- `Failed` — execution failed
|
|
1506
|
+
- `Cancelled` — manually cancelled
|
|
1507
|
+
|
|
1508
|
+
**Status Fields:**
|
|
1509
|
+
- `queuedAt` — when run entered queue
|
|
1510
|
+
- `sseSubscription` — `{ runId, active }` event stream state
|
|
1511
|
+
- `transcriptRef` — reference to AgentSessionTranscript
|
|
1512
|
+
- `conditions[]` — K8s-style condition array
|
|
1513
|
+
|
|
1514
|
+
**Creation Flow (via `createManualDispatch()`):**
|
|
1515
|
+
1. Resolve AgentStack via `resolveStack()` (translates CRD → execution config)
|
|
1516
|
+
2. Permission review (allowed / denied / requires-approval)
|
|
1517
|
+
3. Budget check via `checkBudget()` + `estimateCost()`
|
|
1518
|
+
4. Memory snapshot creation (if AgentMemoryRepository exists)
|
|
1519
|
+
5. Approval gate (if required) — emits `APPROVAL_REQUESTED` hooks event
|
|
1520
|
+
6. Workspace provisioning (reuse existing or create new)
|
|
1521
|
+
7. Context bundle assembly
|
|
1522
|
+
8. Create AgentDispatchRun + AgentDispatchAttempt resources
|
|
1523
|
+
9. `createAgentJob()` generates `batch/v1` Job manifest (image, command, env, volumes, deadline)
|
|
1524
|
+
10. `submitAgentJob()` submits Job to Kubernetes
|
|
1525
|
+
11. Workspace PVC mounted at `/workspace` in agent pod
|
|
1526
|
+
12. Transport env vars injected: `AGENT_MUX_TRANSPORT`, `TRANSPORT_MUX_CODEC`
|
|
1527
|
+
13. Agent pod executes and POSTs result to `/api/orgs/{org}/agents/runs/{name}/callback`
|
|
1528
|
+
14. `persistSessionEvent()` applies result, emits `RUN_COMPLETED` or `RUN_FAILED`
|
|
1529
|
+
|
|
1530
|
+
**K8s Job Relationship:**
|
|
1531
|
+
Each `Running` dispatch run owns exactly one active `batch/v1` Job in Kubernetes.
|
|
1532
|
+
Job name: `agent-{run.metadata.name}`. The `activeDeadlineSeconds` field enforces
|
|
1533
|
+
the budget ceiling: if the agent exceeds the allowed time, Kubernetes terminates
|
|
1534
|
+
the pod and the run transitions to `Failed` (deadline-exceeded).
|
|
1535
|
+
|
|
1536
|
+
**Relationships:**
|
|
1537
|
+
- Belongs to: Organization
|
|
1538
|
+
- References: AgentStack, Repository, KradleWorkspace, AgentContextBundle
|
|
1539
|
+
- Has: AgentDispatchAttempt (1:N), AgentSession (1:1), AgentApproval (0:N)
|
|
1540
|
+
- Produces: KradleArtifact (0:N), AgentSessionTranscript
|
|
1541
|
+
- Linked via: WorkItemSessionLink, WorkItemWorkspaceLink
|
|
1542
|
+
|
|
1543
|
+
---
|
|
1544
|
+
|
|
1545
|
+
#### AgentDispatchAttempt
|
|
1546
|
+
|
|
1547
|
+
| Field | Value |
|
|
1548
|
+
|-------|-------|
|
|
1549
|
+
| Storage | postgres |
|
|
1550
|
+
| Context | agents |
|
|
1551
|
+
| Plural | agentdispatchattempts |
|
|
1552
|
+
| Namespace | org-scoped |
|
|
1553
|
+
|
|
1554
|
+
**Purpose:** Concrete execution attempt with reason, stack snapshot, runtime state,
|
|
1555
|
+
and Agent Mux binding. One run may have multiple attempts (retries).
|
|
1556
|
+
|
|
1557
|
+
**Required Spec Fields:**
|
|
1558
|
+
- `organizationRef` — owning organization
|
|
1559
|
+
- `agentDispatchRun` — reference to parent run
|
|
1560
|
+
- `attemptReason` — why this attempt was created (e.g., 'initial', 'retry')
|
|
1561
|
+
- `agentStackSnapshot` — frozen copy of the stack spec at attempt time
|
|
1562
|
+
|
|
1563
|
+
**Optional Spec Fields:**
|
|
1564
|
+
- `contextBundleDigest` — digest of the context bundle used
|
|
1565
|
+
|
|
1566
|
+
**Status Fields:**
|
|
1567
|
+
- `permissionSnapshot` — captured permission review result
|
|
1568
|
+
- `queueEnteredAt` — when attempt entered queue
|
|
1569
|
+
- `agentMuxRunId` — Agent Mux run identifier
|
|
1570
|
+
- `agentMuxSessionId` — Agent Mux session identifier
|
|
1571
|
+
- `startedAt` — when execution began
|
|
1572
|
+
|
|
1573
|
+
**Relationships:**
|
|
1574
|
+
- Belongs to: AgentDispatchRun (parent)
|
|
1575
|
+
- Creates: AgentSession (1:1 per successful launch)
|
|
1576
|
+
- Contains: frozen stack snapshot for reproducibility
|
|
1577
|
+
|
|
1578
|
+
---
|
|
1579
|
+
|
|
1580
|
+
#### AgentSession
|
|
1581
|
+
|
|
1582
|
+
| Field | Value |
|
|
1583
|
+
|-------|-------|
|
|
1584
|
+
| Storage | postgres |
|
|
1585
|
+
| Context | agents |
|
|
1586
|
+
| Plural | agentsessions |
|
|
1587
|
+
| Namespace | org-scoped |
|
|
1588
|
+
|
|
1589
|
+
**Purpose:** Kradle projection of an Agent Mux chat/session with lifecycle state.
|
|
1590
|
+
Represents a live or completed interaction between an agent and the system.
|
|
1591
|
+
|
|
1592
|
+
**Required Spec Fields:**
|
|
1593
|
+
- `organizationRef` — owning organization
|
|
1594
|
+
- `agentMuxSessionId` �� external Agent Mux session identifier
|
|
1595
|
+
- `dispatchRun` — reference to the parent AgentDispatchRun
|
|
1596
|
+
|
|
1597
|
+
**Lifecycle:**
|
|
1598
|
+
- `Active` — session is live, agent is executing
|
|
1599
|
+
- `Completed` — session ended normally
|
|
1600
|
+
- `Failed` — session ended with error
|
|
1601
|
+
- `Cancelled` — session was manually terminated
|
|
1602
|
+
|
|
1603
|
+
**Relationships:**
|
|
1604
|
+
- Belongs to: AgentDispatchRun
|
|
1605
|
+
- Has: AgentSessionTranscript, AgentSessionAttachment
|
|
1606
|
+
- Linked via: WorkItemSessionLink (to Issues/PRs)
|
|
1607
|
+
- Bound to: KradleWorkspace (via workspace controller)
|
|
1608
|
+
|
|
1609
|
+
---
|
|
1610
|
+
|
|
1611
|
+
#### AgentContextBundle
|
|
1612
|
+
|
|
1613
|
+
| Field | Value |
|
|
1614
|
+
|-------|-------|
|
|
1615
|
+
| Storage | postgres |
|
|
1616
|
+
| Context | agents |
|
|
1617
|
+
| Plural | agentcontextbundles |
|
|
1618
|
+
| Namespace | org-scoped |
|
|
1619
|
+
|
|
1620
|
+
**Purpose:** Immutable prompt/context snapshot with content-addressable digest,
|
|
1621
|
+
provenance tracking, source references, and redaction manifest.
|
|
1622
|
+
|
|
1623
|
+
**Required Spec Fields:**
|
|
1624
|
+
- `organizationRef` — owning organization
|
|
1625
|
+
- `dispatchRun` — reference to the run that created this bundle
|
|
1626
|
+
- `digest` — content-addressable hash of the bundle
|
|
1627
|
+
- `sources` — array of source references (context labels, prompts, etc.)
|
|
1628
|
+
|
|
1629
|
+
**Assembly (via `assembleContextBundle()`):**
|
|
1630
|
+
- Gathers: stack prompts, context labels, repository context, source refs
|
|
1631
|
+
- Applies: redaction scanning for secrets/credentials
|
|
1632
|
+
- Produces: immutable snapshot with digest for cache/dedup
|
|
1633
|
+
|
|
1634
|
+
**Relationships:**
|
|
1635
|
+
- Belongs to: AgentDispatchRun
|
|
1636
|
+
- References: AgentContextLabel (sources)
|
|
1637
|
+
- Contains: assembled prompt content for the agent session
|
|
1638
|
+
|
|
1639
|
+
---
|
|
1640
|
+
|
|
1641
|
+
#### KradleArtifact
|
|
1642
|
+
|
|
1643
|
+
| Field | Value |
|
|
1644
|
+
|-------|-------|
|
|
1645
|
+
| Storage | postgres |
|
|
1646
|
+
| Context | agents |
|
|
1647
|
+
| Plural | kradleartifacts |
|
|
1648
|
+
| Namespace | org-scoped |
|
|
1649
|
+
|
|
1650
|
+
**Purpose:** Durable agent output with kind classification, content-addressable digest,
|
|
1651
|
+
and retention policy. Captures the results of agent work.
|
|
1652
|
+
|
|
1653
|
+
**Required Spec Fields:**
|
|
1654
|
+
- `organizationRef` — owning organization
|
|
1655
|
+
- `dispatchRun` — reference to producing run
|
|
1656
|
+
- `kind` — artifact type (e.g., 'patch', 'report', 'review', 'log')
|
|
1657
|
+
- `digest` — content-addressable hash
|
|
1658
|
+
|
|
1659
|
+
**Optional Spec Fields:**
|
|
1660
|
+
- `retentionPolicy` — how long to keep the artifact
|
|
1661
|
+
- `size` — artifact size in bytes
|
|
1662
|
+
- `mimeType` — content type
|
|
1663
|
+
|
|
1664
|
+
**Relationships:**
|
|
1665
|
+
- Belongs to: AgentDispatchRun (producer)
|
|
1666
|
+
- Stored in: object storage (referenced by digest)
|
|
1667
|
+
- Linked to: Pipeline jobs (for CI artifacts)
|
|
1668
|
+
|
|
1669
|
+
---
|
|
1670
|
+
|
|
1671
|
+
#### AgentApproval
|
|
1672
|
+
|
|
1673
|
+
| Field | Value |
|
|
1674
|
+
|-------|-------|
|
|
1675
|
+
| Storage | postgres |
|
|
1676
|
+
| Context | agents |
|
|
1677
|
+
| Plural | agentapprovals |
|
|
1678
|
+
| Namespace | org-scoped |
|
|
1679
|
+
|
|
1680
|
+
**Purpose:** Human gate for tools, secrets, write-back, and release actions. Implements
|
|
1681
|
+
the approval workflow that pauses agent execution until a human decides.
|
|
1682
|
+
|
|
1683
|
+
**Required Spec Fields:**
|
|
1684
|
+
- `organizationRef` — owning organization
|
|
1685
|
+
- `dispatchRun` — reference to the run requesting approval
|
|
1686
|
+
- `action` — what is being requested: `tool-use`, `secret-access`, `write-back`, `release`, `escalation`
|
|
1687
|
+
- `requestedBy` — who/what triggered the request
|
|
1688
|
+
|
|
1689
|
+
**Optional Spec Fields:**
|
|
1690
|
+
- `description` — context about why approval is needed
|
|
1691
|
+
- `requestedAt` — ISO timestamp
|
|
1692
|
+
|
|
1693
|
+
**Lifecycle Phases:**
|
|
1694
|
+
- `Pending` — awaiting human decision
|
|
1695
|
+
- `Approved` — action allowed to proceed
|
|
1696
|
+
- `Denied` — action blocked
|
|
1697
|
+
|
|
1698
|
+
**Operations:**
|
|
1699
|
+
- `createApprovalRequest()` — creates with dedup check (no duplicate pending for same run+action)
|
|
1700
|
+
- `recordDecision()` — records approve/deny with decidedBy and reason
|
|
1701
|
+
- `isActionApproved()` — checks if action is approved for a run
|
|
1702
|
+
- `enforceApproval()` — enforcement gate (allowed/denied/pending)
|
|
1703
|
+
- `persistApproval()` — persists via applyResource
|
|
1704
|
+
- `listPendingApprovals()` — finds all pending approvals for an org
|
|
1705
|
+
- `listApprovalsForRun()` — finds all approvals for a dispatch run
|
|
1706
|
+
|
|
1707
|
+
**Relationships:**
|
|
1708
|
+
- Belongs to: AgentDispatchRun
|
|
1709
|
+
- Blocks: agent execution until decided
|
|
1710
|
+
- Created by: dispatch controller (on requires-approval review)
|
|
1711
|
+
- Decided by: human operator via UI or API
|
|
1712
|
+
|
|
1713
|
+
---
|
|
1714
|
+
|
|
1715
|
+
#### AgentTriggerExecution
|
|
1716
|
+
|
|
1717
|
+
| Field | Value |
|
|
1718
|
+
|-------|-------|
|
|
1719
|
+
| Storage | postgres |
|
|
1720
|
+
| Context | agents |
|
|
1721
|
+
| Plural | agenttriggerexecutions |
|
|
1722
|
+
| Namespace | org-scoped |
|
|
1723
|
+
|
|
1724
|
+
**Purpose:** Durable trigger evaluation record with deduplication state, coalescing
|
|
1725
|
+
decisions, and rejection reasons. Provides audit trail for trigger matching.
|
|
1726
|
+
|
|
1727
|
+
**Required Spec Fields:**
|
|
1728
|
+
- `organizationRef` — owning organization
|
|
1729
|
+
- `triggerRule` — reference to the evaluated AgentTriggerRule
|
|
1730
|
+
- `sourceEvent` — event UID (`type:kind:name`)
|
|
1731
|
+
- `decision` — evaluation outcome
|
|
1732
|
+
|
|
1733
|
+
**Decision Values:**
|
|
1734
|
+
- `Dispatching` / `Dispatched` — trigger matched, run created
|
|
1735
|
+
- `Skipped` — trigger did not match (with reason)
|
|
1736
|
+
- `Deduplicated` — would match but identical execution already exists
|
|
1737
|
+
- `Failed` — dispatch attempted but failed
|
|
1738
|
+
|
|
1739
|
+
**Status Fields:**
|
|
1740
|
+
- `phase` — mirrors decision
|
|
1741
|
+
- `reason` — explanation of the decision
|
|
1742
|
+
- `evaluatedAt` — when evaluation occurred
|
|
1743
|
+
- `dispatchRunRef` — reference to created run (if dispatched)
|
|
1744
|
+
|
|
1745
|
+
**Relationships:**
|
|
1746
|
+
- References: AgentTriggerRule (the rule that was evaluated)
|
|
1747
|
+
- Produces: AgentDispatchRun (on successful dispatch)
|
|
1748
|
+
- Used for: deduplication of subsequent identical events
|
|
1749
|
+
|
|
1750
|
+
---
|
|
1751
|
+
|
|
1752
|
+
#### AgentCapabilityRequirement
|
|
1753
|
+
|
|
1754
|
+
| Field | Value |
|
|
1755
|
+
|-------|-------|
|
|
1756
|
+
| Storage | postgres |
|
|
1757
|
+
| Context | agents |
|
|
1758
|
+
| Plural | agentcapabilityrequirements |
|
|
1759
|
+
| Namespace | org-scoped |
|
|
1760
|
+
|
|
1761
|
+
**Purpose:** Computed dependency record derived from tools, MCP servers, skills, models,
|
|
1762
|
+
and subagents. Represents what capabilities an agent needs to function.
|
|
1763
|
+
|
|
1764
|
+
**Required Spec Fields:**
|
|
1765
|
+
- `organizationRef` — owning organization
|
|
1766
|
+
- `ownerRef` — reference to the requiring resource (usually AgentStack)
|
|
1767
|
+
- `requiredRoles` — array of required role/capability identifiers
|
|
1768
|
+
|
|
1769
|
+
**Relationships:**
|
|
1770
|
+
- Belongs to: AgentStack (owner)
|
|
1771
|
+
- Checked during: stack reconciliation and dispatch permission review
|
|
1772
|
+
|
|
1773
|
+
---
|
|
1774
|
+
|
|
1775
|
+
#### WorkItemSessionLink
|
|
1776
|
+
|
|
1777
|
+
| Field | Value |
|
|
1778
|
+
|-------|-------|
|
|
1779
|
+
| Storage | postgres |
|
|
1780
|
+
| Context | agents |
|
|
1781
|
+
| Plural | workitemsessionlinks |
|
|
1782
|
+
| Namespace | org-scoped |
|
|
1783
|
+
|
|
1784
|
+
**Purpose:** Association between issues/PRs and agent sessions. Tracks which agent
|
|
1785
|
+
sessions worked on which work items.
|
|
1786
|
+
|
|
1787
|
+
**Required Spec Fields:**
|
|
1788
|
+
- `organizationRef` — owning organization
|
|
1789
|
+
- `workItemRef` — reference to Issue or PullRequest
|
|
1790
|
+
- `agentSession` — reference to AgentSession
|
|
1791
|
+
|
|
1792
|
+
**Creation:**
|
|
1793
|
+
- `linkWorkItemToSession()` in workspace controller
|
|
1794
|
+
- Auto-generated name: `wisl-{sessionRef}-{workItemRef}-{timestamp}`
|
|
1795
|
+
|
|
1796
|
+
**Relationships:**
|
|
1797
|
+
- Links: Issue/PullRequest ↔ AgentSession
|
|
1798
|
+
- Used by: project boards to show which sessions worked on which items
|
|
1799
|
+
- Enables: session history view per work item
|
|
1800
|
+
|
|
1801
|
+
---
|
|
1802
|
+
|
|
1803
|
+
#### WorkItemWorkspaceLink
|
|
1804
|
+
|
|
1805
|
+
| Field | Value |
|
|
1806
|
+
|-------|-------|
|
|
1807
|
+
| Storage | postgres |
|
|
1808
|
+
| Context | agents |
|
|
1809
|
+
| Plural | workitemworkspacelinks |
|
|
1810
|
+
| Namespace | org-scoped |
|
|
1811
|
+
|
|
1812
|
+
**Purpose:** Association between issues/PRs and agent workspaces. Tracks which
|
|
1813
|
+
workspaces contain work related to which items.
|
|
1814
|
+
|
|
1815
|
+
**Required Spec Fields:**
|
|
1816
|
+
- `organizationRef` — owning organization
|
|
1817
|
+
- `workItemRef` — reference to Issue or PullRequest
|
|
1818
|
+
- `workspace` — reference to KradleWorkspace
|
|
1819
|
+
|
|
1820
|
+
**Optional Spec Fields:**
|
|
1821
|
+
- `workItemKind` — `Issue` or `PullRequest`
|
|
1822
|
+
|
|
1823
|
+
**Creation:**
|
|
1824
|
+
- `linkWorkItem()` in workspace controller
|
|
1825
|
+
- Auto-generated name: `wiwl-{workspaceName}-{workItemRef}-{timestamp}`
|
|
1826
|
+
|
|
1827
|
+
**Relationships:**
|
|
1828
|
+
- Links: Issue/PullRequest ↔ KradleWorkspace
|
|
1829
|
+
- Used by: workspace controller for run history queries
|
|
1830
|
+
|
|
1831
|
+
---
|
|
1832
|
+
|
|
1833
|
+
#### AgentSessionTranscript
|
|
1834
|
+
|
|
1835
|
+
| Field | Value |
|
|
1836
|
+
|-------|-------|
|
|
1837
|
+
| Storage | postgres |
|
|
1838
|
+
| Context | agents |
|
|
1839
|
+
| Plural | agentsessiontranscripts |
|
|
1840
|
+
| Namespace | org-scoped |
|
|
1841
|
+
|
|
1842
|
+
**Purpose:** Durable chat transcript with message nodes, pagination support, and
|
|
1843
|
+
cost-per-turn tracking. Stores the full conversation history.
|
|
1844
|
+
|
|
1845
|
+
**Required Spec Fields:**
|
|
1846
|
+
- `organizationRef` — owning organization
|
|
1847
|
+
- `sessionRef` — reference to AgentSession
|
|
1848
|
+
- `messages` — array of message nodes
|
|
1849
|
+
|
|
1850
|
+
**Message Node Structure:**
|
|
1851
|
+
- `role` — user/assistant/system/tool
|
|
1852
|
+
- `content` — message content
|
|
1853
|
+
- `timestamp` — when the message was sent
|
|
1854
|
+
- `costTokens` — token usage for this turn
|
|
1855
|
+
- `toolCalls[]` — tool invocations (if any)
|
|
1856
|
+
|
|
1857
|
+
**Reconciliation:**
|
|
1858
|
+
- Created by `agentMuxClient.reconcileTranscript()`
|
|
1859
|
+
- Updated as SSE events stream in from Agent Mux
|
|
1860
|
+
- Referenced by AgentDispatchRun.status.transcriptRef
|
|
1861
|
+
|
|
1862
|
+
**Relationships:**
|
|
1863
|
+
- Belongs to: AgentSession
|
|
1864
|
+
- Contains: full conversation history
|
|
1865
|
+
- Updated via: SSE event streaming from Agent Mux
|
|
1866
|
+
|
|
1867
|
+
---
|
|
1868
|
+
|
|
1869
|
+
#### AgentSessionAttachment
|
|
1870
|
+
|
|
1871
|
+
| Field | Value |
|
|
1872
|
+
|-------|-------|
|
|
1873
|
+
| Storage | postgres |
|
|
1874
|
+
| Context | agents |
|
|
1875
|
+
| Plural | agentsessionattachments |
|
|
1876
|
+
| Namespace | org-scoped |
|
|
1877
|
+
|
|
1878
|
+
**Purpose:** File attached to a session message with source type, MIME type, digest,
|
|
1879
|
+
and redaction status.
|
|
1880
|
+
|
|
1881
|
+
**Required Spec Fields:**
|
|
1882
|
+
- `organizationRef` — owning organization
|
|
1883
|
+
- `sessionRef` — reference to AgentSession
|
|
1884
|
+
- `sourceType` — where the file came from (e.g., 'workspace', 'upload', 'tool-output')
|
|
1885
|
+
- `digest` — content-addressable hash
|
|
1886
|
+
|
|
1887
|
+
**Optional Spec Fields:**
|
|
1888
|
+
- `mimeType` — MIME type
|
|
1889
|
+
- `size` — file size
|
|
1890
|
+
- `redacted` — boolean indicating if content was redacted
|
|
1891
|
+
- `originalPath` — original file path in workspace
|
|
1892
|
+
|
|
1893
|
+
**Relationships:**
|
|
1894
|
+
- Belongs to: AgentSession
|
|
1895
|
+
- Stored in: object storage (referenced by digest)
|
|
1896
|
+
- May reference: KradleWorkspace files
|
|
1897
|
+
|
|
1898
|
+
---
|
|
1899
|
+
|
|
1900
|
+
### 1.6 Workspace Domain (3 kinds)
|
|
1901
|
+
|
|
1902
|
+
---
|
|
1903
|
+
|
|
1904
|
+
#### KradleWorkspace
|
|
1905
|
+
|
|
1906
|
+
| Field | Value |
|
|
1907
|
+
|-------|-------|
|
|
1908
|
+
| Storage | etcd |
|
|
1909
|
+
| Context | workspaces |
|
|
1910
|
+
| Plural | kradleworkspaces |
|
|
1911
|
+
| Namespace | org-scoped |
|
|
1912
|
+
|
|
1913
|
+
**Purpose:** Volume-backed git workspace with PVC lifecycle, repository binding, agent
|
|
1914
|
+
Job mount spec, session associations, and run history.
|
|
1915
|
+
|
|
1916
|
+
**Required Spec Fields:**
|
|
1917
|
+
- `organizationRef` — owning organization
|
|
1918
|
+
- `repository` — bound git repository
|
|
1919
|
+
- `volumeSpec` — PVC specification `{ storageClassName, capacity, accessModes }`
|
|
1920
|
+
|
|
1921
|
+
**Optional Spec Fields:**
|
|
1922
|
+
- `branch` — checked-out branch (default: 'main')
|
|
1923
|
+
- `pvcName` — PVC name (auto-generated: `kradle-ws-{name}`)
|
|
1924
|
+
- `associations[]` — `[{ kind, name, addedAt }]` linked resources
|
|
1925
|
+
|
|
1926
|
+
**Lifecycle Phases:**
|
|
1927
|
+
- `Pending` — workspace created, PVC not yet bound
|
|
1928
|
+
- `Provisioning` — PVC being created
|
|
1929
|
+
- `Ready` — workspace available for use
|
|
1930
|
+
- `InUse` — claimed by a dispatch run; PVC mounted at `/workspace` in agent Job pod
|
|
1931
|
+
- `Released` — run completed, workspace available again
|
|
1932
|
+
- `Archived` — long-term storage, not active
|
|
1933
|
+
- `Terminating` — being deleted
|
|
1934
|
+
|
|
1935
|
+
**Agent Job Mounting:**
|
|
1936
|
+
When an `AgentDispatchRun` enters the `Running` phase, the workspace PVC is mounted
|
|
1937
|
+
into the agent's `batch/v1` Job pod at `/workspace`. `getMountSpec()` returns the
|
|
1938
|
+
`{ volume, volumeMount }` pair that `createAgentJob()` injects into the Job template:
|
|
1939
|
+
|
|
1940
|
+
```yaml
|
|
1941
|
+
volumes:
|
|
1942
|
+
- name: workspace
|
|
1943
|
+
persistentVolumeClaim:
|
|
1944
|
+
claimName: kradle-ws-{workspaceName}
|
|
1945
|
+
containers:
|
|
1946
|
+
- name: agent
|
|
1947
|
+
volumeMounts:
|
|
1948
|
+
- name: workspace
|
|
1949
|
+
mountPath: /workspace
|
|
1950
|
+
```
|
|
1951
|
+
|
|
1952
|
+
The agent process reads source code, writes changes, and produces artifacts relative
|
|
1953
|
+
to `/workspace`. This path is fixed regardless of which workspace PVC is mounted.
|
|
1954
|
+
|
|
1955
|
+
**Operations:**
|
|
1956
|
+
- `createWorkspace()` — creates workspace + PVC manifest
|
|
1957
|
+
- `deleteWorkspace()` — marks Terminating, generates PVC delete manifest
|
|
1958
|
+
- `claimWorkspace()` — transitions Ready → InUse with runRef
|
|
1959
|
+
- `releaseWorkspace()` — transitions InUse → Ready
|
|
1960
|
+
- `archiveWorkspace()` — transitions to Archived
|
|
1961
|
+
- `recoverWorkspace()` — transitions Archived → Active
|
|
1962
|
+
- `findReusableWorkspace()` — finds Ready workspace matching org+repo+branch
|
|
1963
|
+
- `initializeWorkspace()` — generates git clone command spec
|
|
1964
|
+
- `checkoutBranch()` — generates git checkout command spec
|
|
1965
|
+
- `syncWorkspace()` — generates git fetch + reset command specs
|
|
1966
|
+
- `getMountSpec()` — generates PVC volume + volumeMount for pod specs
|
|
1967
|
+
- `bindSession()` — records session binding in status
|
|
1968
|
+
- `linkWorkItem()` — creates WorkItemWorkspaceLink
|
|
1969
|
+
- `linkWorkItemToSession()` — creates WorkItemSessionLink
|
|
1970
|
+
- `addAssociation()` — adds resource association (AgentDispatchRun, User, AgentSession)
|
|
1971
|
+
- `removeAssociation()` — removes resource association
|
|
1972
|
+
- `listAssociations()` — returns all associations
|
|
1973
|
+
- `getWorkspaceRuns()` — returns active and historical runs for this workspace
|
|
1974
|
+
|
|
1975
|
+
**Codespace Operations:**
|
|
1976
|
+
- `launchCodespace()` — generates Pod + Service specs for code-server IDE
|
|
1977
|
+
- `stopCodespace()` — generates delete manifests for codespace pod/service
|
|
1978
|
+
- `getCodespaceStatus()` — reports codespace running state and URL
|
|
1979
|
+
|
|
1980
|
+
**PVC Generation:**
|
|
1981
|
+
```yaml
|
|
1982
|
+
apiVersion: v1
|
|
1983
|
+
kind: PersistentVolumeClaim
|
|
1984
|
+
metadata:
|
|
1985
|
+
name: kradle-ws-{workspaceName}
|
|
1986
|
+
labels:
|
|
1987
|
+
kradle.a5c.ai/workspace: {workspaceName}
|
|
1988
|
+
kradle.a5c.ai/org: {organizationRef}
|
|
1989
|
+
spec:
|
|
1990
|
+
storageClassName: standard
|
|
1991
|
+
accessModes: [ReadWriteOnce]
|
|
1992
|
+
resources:
|
|
1993
|
+
requests:
|
|
1994
|
+
storage: 10Gi
|
|
1995
|
+
```
|
|
1996
|
+
|
|
1997
|
+
**Relationships:**
|
|
1998
|
+
- Belongs to: Organization, Repository
|
|
1999
|
+
- Used by: AgentDispatchRun, RunnerPool pods
|
|
2000
|
+
- Contains: git worktree with checked-out code
|
|
2001
|
+
- Linked via: WorkItemWorkspaceLink (to Issues/PRs)
|
|
2002
|
+
|
|
2003
|
+
---
|
|
2004
|
+
|
|
2005
|
+
#### KradleWorkspacePolicy
|
|
2006
|
+
|
|
2007
|
+
(Documented above in Agent Domain - section 1.5)
|
|
2008
|
+
|
|
2009
|
+
---
|
|
2010
|
+
|
|
2011
|
+
#### KradleWorkspaceRuntime
|
|
2012
|
+
|
|
2013
|
+
| Field | Value |
|
|
2014
|
+
|-------|-------|
|
|
2015
|
+
| Storage | postgres |
|
|
2016
|
+
| Context | agents |
|
|
2017
|
+
| Plural | kradleworkspaceruntimes |
|
|
2018
|
+
| Namespace | org-scoped |
|
|
2019
|
+
|
|
2020
|
+
**Purpose:** Workspace runtime surface state with current working directory, environment
|
|
2021
|
+
variables, process status, and preview URL.
|
|
2022
|
+
|
|
2023
|
+
**Required Spec Fields:**
|
|
2024
|
+
- `organizationRef` — owning organization
|
|
2025
|
+
- `workspaceRef` — reference to KradleWorkspace
|
|
2026
|
+
- `status` — current runtime status
|
|
2027
|
+
|
|
2028
|
+
**Status Fields:**
|
|
2029
|
+
- `phase` — Provisioning | Active | Stopped
|
|
2030
|
+
- `cwd` — current working directory
|
|
2031
|
+
- `env` — environment variables map
|
|
2032
|
+
- `processStatus` — running process info
|
|
2033
|
+
- `previewUrl` — if workspace exposes a preview
|
|
2034
|
+
- `createdAt` — creation timestamp
|
|
2035
|
+
|
|
2036
|
+
**Creation:**
|
|
2037
|
+
- Created by `provisionWorkspace()` alongside KradleWorkspace
|
|
2038
|
+
- Updated as workspace state changes
|
|
2039
|
+
|
|
2040
|
+
**Relationships:**
|
|
2041
|
+
- Belongs to: KradleWorkspace
|
|
2042
|
+
- Reflects: runtime state of the workspace pod
|
|
2043
|
+
|
|
2044
|
+
---
|
|
2045
|
+
|
|
2046
|
+
### 1.7 Memory Domain (7 kinds)
|
|
2047
|
+
|
|
2048
|
+
Resources for the "Company Brain" — organization-wide agent memory with graph/grep
|
|
2049
|
+
search, time-travel, imports, ontology validation, and snapshot pinning.
|
|
2050
|
+
|
|
2051
|
+
---
|
|
2052
|
+
|
|
2053
|
+
#### AgentMemoryRepository
|
|
2054
|
+
|
|
2055
|
+
| Field | Value |
|
|
2056
|
+
|-------|-------|
|
|
2057
|
+
| Storage | etcd |
|
|
2058
|
+
| Context | agents |
|
|
2059
|
+
| Plural | agentmemoryrepositories |
|
|
2060
|
+
| Namespace | org-scoped |
|
|
2061
|
+
|
|
2062
|
+
**Purpose:** Organization-level Git repository pointer for shared agent memory with
|
|
2063
|
+
layout profile and index policy. The memory repository contains structured knowledge
|
|
2064
|
+
that agents can query.
|
|
2065
|
+
|
|
2066
|
+
**Required Spec Fields:**
|
|
2067
|
+
- `organizationRef` — owning organization
|
|
2068
|
+
- `repositoryRef` — reference to a Repository resource (the git repo storing memory)
|
|
2069
|
+
- `defaultBranch` — branch to read from (e.g., 'main')
|
|
2070
|
+
- `layoutProfile` — how memory is organized in the repo (e.g., 'flat', 'hierarchical')
|
|
2071
|
+
|
|
2072
|
+
**Optional Spec Fields:**
|
|
2073
|
+
- `indexPolicy` — how and when to rebuild indexes
|
|
2074
|
+
- `retentionPolicy` — how long to keep old memory
|
|
2075
|
+
|
|
2076
|
+
**Gitea Naming Convention:**
|
|
2077
|
+
- `orgMemoryRepositoryName(org)` → `_${org}_` (e.g., `_default_`)
|
|
2078
|
+
- Used as the Gitea repo name for memory storage
|
|
2079
|
+
|
|
2080
|
+
**Relationships:**
|
|
2081
|
+
- Belongs to: Organization
|
|
2082
|
+
- References: Repository (the backing git repo)
|
|
2083
|
+
- Has: AgentMemorySource (read policies), AgentMemoryOntology (schema)
|
|
2084
|
+
- Produces: AgentMemorySnapshot (at dispatch time)
|
|
2085
|
+
|
|
2086
|
+
---
|
|
2087
|
+
|
|
2088
|
+
#### AgentMemorySource
|
|
2089
|
+
|
|
2090
|
+
| Field | Value |
|
|
2091
|
+
|-------|-------|
|
|
2092
|
+
| Storage | etcd |
|
|
2093
|
+
| Context | agents |
|
|
2094
|
+
| Plural | agentmemorysources |
|
|
2095
|
+
| Namespace | org-scoped |
|
|
2096
|
+
|
|
2097
|
+
**Purpose:** Read policy for memory paths and kinds per repository, team, stack, or
|
|
2098
|
+
trigger. Controls which parts of memory are available to which consumers.
|
|
2099
|
+
|
|
2100
|
+
**Required Spec Fields:**
|
|
2101
|
+
- `organizationRef` — owning organization
|
|
2102
|
+
- `repositoryRef` — which memory repository this applies to
|
|
2103
|
+
- `appliesTo` — scope (`{ kind, name }` — e.g., `{ kind: 'AgentStack', name: 'my-stack' }`)
|
|
2104
|
+
- `include` — paths/patterns to include
|
|
2105
|
+
|
|
2106
|
+
**Optional Spec Fields:**
|
|
2107
|
+
- `exclude` — paths/patterns to exclude
|
|
2108
|
+
- `kinds[]` — node kinds to include in graph queries
|
|
2109
|
+
|
|
2110
|
+
**Relationships:**
|
|
2111
|
+
- Belongs to: AgentMemoryRepository
|
|
2112
|
+
- Controls: what memory content is visible to specific stacks/teams
|
|
2113
|
+
|
|
2114
|
+
---
|
|
2115
|
+
|
|
2116
|
+
#### AgentMemoryOntology
|
|
2117
|
+
|
|
2118
|
+
| Field | Value |
|
|
2119
|
+
|-------|-------|
|
|
2120
|
+
| Storage | etcd |
|
|
2121
|
+
| Context | agents |
|
|
2122
|
+
| Plural | agentmemoryontologies |
|
|
2123
|
+
| Namespace | org-scoped |
|
|
2124
|
+
|
|
2125
|
+
**Purpose:** Ontology policy pointer with required fields, edge kinds, and controlled
|
|
2126
|
+
vocabulary. Defines the schema that memory records must conform to.
|
|
2127
|
+
|
|
2128
|
+
**Required Spec Fields:**
|
|
2129
|
+
- `organizationRef` — owning organization
|
|
2130
|
+
- `memoryRepository` — reference to AgentMemoryRepository
|
|
2131
|
+
- `ontologyPath` — path within the memory repo where ontology is defined
|
|
2132
|
+
|
|
2133
|
+
**Ontology Structure:**
|
|
2134
|
+
- `requiredFields` — map of nodeKind → required field names
|
|
2135
|
+
- `allowedEdgeKinds` — array of valid edge relationship types
|
|
2136
|
+
- `controlledVocabulary` — terms that must be used consistently
|
|
2137
|
+
|
|
2138
|
+
**Validation (`validateOntology()`):**
|
|
2139
|
+
- Checks all records have required fields for their nodeKind
|
|
2140
|
+
- Checks all edge kinds are in allowedEdgeKinds list
|
|
2141
|
+
|
|
2142
|
+
**Relationships:**
|
|
2143
|
+
- Belongs to: AgentMemoryRepository
|
|
2144
|
+
- Enforced on: AgentMemoryUpdate submissions
|
|
2145
|
+
- Checked during: import processing
|
|
2146
|
+
|
|
2147
|
+
---
|
|
2148
|
+
|
|
2149
|
+
#### AgentMemoryAssociation
|
|
2150
|
+
|
|
2151
|
+
| Field | Value |
|
|
2152
|
+
|-------|-------|
|
|
2153
|
+
| Storage | etcd |
|
|
2154
|
+
| Context | agents |
|
|
2155
|
+
| Plural | agentmemoryassociations |
|
|
2156
|
+
| Namespace | org-scoped |
|
|
2157
|
+
|
|
2158
|
+
**Purpose:** Bridge record linking memory content to Kradle resources by relationship type.
|
|
2159
|
+
Enables bidirectional navigation between memory and resources.
|
|
2160
|
+
|
|
2161
|
+
**Required Spec Fields:**
|
|
2162
|
+
- `organizationRef` — owning organization
|
|
2163
|
+
- `memoryRef` — reference to a memory record (path or ID)
|
|
2164
|
+
- `targetRef` — reference to a Kradle resource (`{ kind, name }`)
|
|
2165
|
+
- `relationship` — type of relationship (e.g., 'documents', 'implements', 'relates-to')
|
|
2166
|
+
|
|
2167
|
+
**Relationships:**
|
|
2168
|
+
- Links: memory records ↔ Kradle resources
|
|
2169
|
+
- Enables: context-aware memory retrieval during dispatch
|
|
2170
|
+
|
|
2171
|
+
---
|
|
2172
|
+
|
|
2173
|
+
#### AgentMemorySnapshot
|
|
2174
|
+
|
|
2175
|
+
| Field | Value |
|
|
2176
|
+
|-------|-------|
|
|
2177
|
+
| Storage | postgres |
|
|
2178
|
+
| Context | agents |
|
|
2179
|
+
| Plural | agentmemorysnapshots |
|
|
2180
|
+
| Namespace | org-scoped |
|
|
2181
|
+
|
|
2182
|
+
**Purpose:** Immutable dispatch-time memory pin with resolved commit, query manifest
|
|
2183
|
+
digest, and selected records digest. Captures the exact memory state used for a run.
|
|
2184
|
+
|
|
2185
|
+
**Required Spec Fields:**
|
|
2186
|
+
- `organizationRef` — owning organization
|
|
2187
|
+
- `memoryRepository` — which memory repository was snapshotted
|
|
2188
|
+
- `requestedRef` — the ref that was requested (branch, tag, commit)
|
|
2189
|
+
- `resolvedCommit` — the actual commit SHA used
|
|
2190
|
+
|
|
2191
|
+
**Optional Spec Fields:**
|
|
2192
|
+
- `queryManifestDigest` — hash of the query parameters used
|
|
2193
|
+
- `selectedRecordsDigest` — hash of selected graph records
|
|
2194
|
+
- `selectedDocumentsDigest` — hash of selected grep documents
|
|
2195
|
+
- `ontologyDigest` — hash of ontology at snapshot time
|
|
2196
|
+
- `recordCount` — number of selected records
|
|
2197
|
+
- `documentCount` — number of selected documents
|
|
2198
|
+
|
|
2199
|
+
**Time-Travel Modes (`resolveTimeTravel()`):**
|
|
2200
|
+
- `current` — use latest commit
|
|
2201
|
+
- `explicit-ref` — use specified ref directly
|
|
2202
|
+
- `ref-at-time` — find commit closest to but not after target time
|
|
2203
|
+
- `snapshot-tag` — use tagged snapshot commit
|
|
2204
|
+
|
|
2205
|
+
**Relationships:**
|
|
2206
|
+
- Belongs to: AgentDispatchRun (via memorySnapshotRef)
|
|
2207
|
+
- References: AgentMemoryRepository
|
|
2208
|
+
- Pinned at: specific git commit for reproducibility
|
|
2209
|
+
|
|
2210
|
+
---
|
|
2211
|
+
|
|
2212
|
+
#### AgentMemoryQuery
|
|
2213
|
+
|
|
2214
|
+
| Field | Value |
|
|
2215
|
+
|-------|-------|
|
|
2216
|
+
| Storage | postgres |
|
|
2217
|
+
| Context | agents |
|
|
2218
|
+
| Plural | agentmemoryqueries |
|
|
2219
|
+
| Namespace | org-scoped |
|
|
2220
|
+
|
|
2221
|
+
**Purpose:** Graph and grep retrieval record with query parameters, result digests,
|
|
2222
|
+
and ranking metadata. Logs what queries were executed and their results.
|
|
2223
|
+
|
|
2224
|
+
**Required Spec Fields:**
|
|
2225
|
+
- `organizationRef` — owning organization
|
|
2226
|
+
- `snapshotRef` — reference to AgentMemorySnapshot used
|
|
2227
|
+
- `requester` — who/what executed the query
|
|
2228
|
+
- `query` — query parameters object
|
|
2229
|
+
|
|
2230
|
+
**Query Modes:**
|
|
2231
|
+
- `graph-only` — search graph records only
|
|
2232
|
+
- `grep-only` — search document content only
|
|
2233
|
+
- `graph-and-grep` — both (default)
|
|
2234
|
+
|
|
2235
|
+
**Graph Search:**
|
|
2236
|
+
- Filters by `kinds[]` (node kinds)
|
|
2237
|
+
- Traverses edges up to `edgeDepth` levels
|
|
2238
|
+
- Text matching against record content
|
|
2239
|
+
- Returns: `{ matches: [{ record, score, edges }], totalMatches }`
|
|
2240
|
+
|
|
2241
|
+
**Grep Search:**
|
|
2242
|
+
- Filters by `paths[]` patterns
|
|
2243
|
+
- Pattern matching against document content
|
|
2244
|
+
- Limited by `maxMatches` (default: 25)
|
|
2245
|
+
- Returns: `{ excerpts: [...], totalMatches }`
|
|
2246
|
+
|
|
2247
|
+
**Relationships:**
|
|
2248
|
+
- Belongs to: AgentMemorySnapshot
|
|
2249
|
+
- Records: query execution history for auditing
|
|
2250
|
+
|
|
2251
|
+
---
|
|
2252
|
+
|
|
2253
|
+
#### AgentMemoryUpdate
|
|
2254
|
+
|
|
2255
|
+
| Field | Value |
|
|
2256
|
+
|-------|-------|
|
|
2257
|
+
| Storage | postgres |
|
|
2258
|
+
| Context | agents |
|
|
2259
|
+
| Plural | agentmemoryupdates |
|
|
2260
|
+
| Namespace | org-scoped |
|
|
2261
|
+
|
|
2262
|
+
**Purpose:** Reviewable proposed memory mutation with branch, changes, and validation
|
|
2263
|
+
status. Represents an agent's request to update the memory repository.
|
|
2264
|
+
|
|
2265
|
+
**Required Spec Fields:**
|
|
2266
|
+
- `organizationRef` — owning organization
|
|
2267
|
+
- `memoryRepository` — target memory repository
|
|
2268
|
+
- `sourceRun` — reference to the run proposing changes
|
|
2269
|
+
- `changes` — array of proposed changes
|
|
2270
|
+
|
|
2271
|
+
**Lifecycle Phases:**
|
|
2272
|
+
- `Pending` — update proposed, awaiting review
|
|
2273
|
+
- `Validated` — ontology checks passed
|
|
2274
|
+
- `Approved` — human approved the changes
|
|
2275
|
+
- `Committed` — changes committed to memory repo
|
|
2276
|
+
- `Rejected` — changes rejected
|
|
2277
|
+
|
|
2278
|
+
**Relationships:**
|
|
2279
|
+
- References: AgentMemoryRepository, AgentDispatchRun (source)
|
|
2280
|
+
- Validated against: AgentMemoryOntology
|
|
2281
|
+
- On commit: creates git commit in memory repository
|
|
2282
|
+
|
|
2283
|
+
---
|
|
2284
|
+
|
|
2285
|
+
#### AgentRunMemoryImport
|
|
2286
|
+
|
|
2287
|
+
| Field | Value |
|
|
2288
|
+
|-------|-------|
|
|
2289
|
+
| Storage | postgres |
|
|
2290
|
+
| Context | agents |
|
|
2291
|
+
| Plural | agentrunmemoryimports |
|
|
2292
|
+
| Namespace | org-scoped |
|
|
2293
|
+
|
|
2294
|
+
**Purpose:** Import curated babysitter run metadata into the organization's company brain
|
|
2295
|
+
with redaction and review workflow.
|
|
2296
|
+
|
|
2297
|
+
**Required Spec Fields:**
|
|
2298
|
+
- `organizationRef` — owning organization
|
|
2299
|
+
- `memoryRepository` — target memory repository
|
|
2300
|
+
- `source` — source of the import (run reference, external URL, etc.)
|
|
2301
|
+
- `include` — what to include from the source
|
|
2302
|
+
|
|
2303
|
+
**Optional Spec Fields:**
|
|
2304
|
+
- `validationPolicy` — validation rules to apply (default: 'none')
|
|
2305
|
+
|
|
2306
|
+
**Import Pipeline Phases:**
|
|
2307
|
+
1. `Pending` — import created
|
|
2308
|
+
2. `Collecting` — gathering content from source
|
|
2309
|
+
3. `Redacting` — scanning for secrets/credentials
|
|
2310
|
+
4. `Normalizing` — converting to memory format
|
|
2311
|
+
5. `Validating` — checking against ontology
|
|
2312
|
+
6. `AwaitingReview` — ready for human review
|
|
2313
|
+
|
|
2314
|
+
**Redaction Scanning (`scanForRedaction()`):**
|
|
2315
|
+
Detects and replaces:
|
|
2316
|
+
- Secret keys (API_KEY=, PASSWORD=, etc.)
|
|
2317
|
+
- Provider tokens (sk-*, ghp_*, glpat-*, xoxb-*, etc.)
|
|
2318
|
+
- Bearer tokens
|
|
2319
|
+
- Private keys (PEM format)
|
|
2320
|
+
- Base64 credentials (40+ chars)
|
|
2321
|
+
|
|
2322
|
+
**Relationships:**
|
|
2323
|
+
- References: AgentMemoryRepository
|
|
2324
|
+
- Source: babysitter runs, external data
|
|
2325
|
+
- Validated by: AgentMemoryOntology
|
|
2326
|
+
- Produces: memory records on approval
|
|
2327
|
+
|
|
2328
|
+
---
|
|
2329
|
+
|
|
2330
|
+
### 1.8 External Backend Domain (10 kinds)
|
|
2331
|
+
|
|
2332
|
+
Resources managing bidirectional synchronization with external providers (GitHub, GitLab,
|
|
2333
|
+
etc.) including provider registration, binding, sync policy, webhook delivery,
|
|
2334
|
+
event normalization, state tracking, write intents, conflict resolution, object linking,
|
|
2335
|
+
and capability manifests.
|
|
2336
|
+
|
|
2337
|
+
---
|
|
2338
|
+
|
|
2339
|
+
#### ExternalBackendProvider
|
|
2340
|
+
|
|
2341
|
+
| Field | Value |
|
|
2342
|
+
|-------|-------|
|
|
2343
|
+
| Storage | etcd |
|
|
2344
|
+
| Context | external-backends |
|
|
2345
|
+
| Plural | externalbackendproviders |
|
|
2346
|
+
| Namespace | org-scoped |
|
|
2347
|
+
|
|
2348
|
+
**Purpose:** External backend provider registration with type, endpoint, auth
|
|
2349
|
+
configuration, and capability discovery settings.
|
|
2350
|
+
|
|
2351
|
+
**Required Spec Fields:**
|
|
2352
|
+
- `organizationRef` — owning organization
|
|
2353
|
+
- `providerType` — provider identifier (e.g., 'github', 'gitlab', 'gitea')
|
|
2354
|
+
- `endpoint` — provider API endpoint URL
|
|
2355
|
+
|
|
2356
|
+
**Optional Spec Fields:**
|
|
2357
|
+
- `displayName` — human-readable provider name
|
|
2358
|
+
- `config` — provider-specific configuration
|
|
2359
|
+
- `authConfig` — authentication settings (app ID, installation ID, etc.)
|
|
2360
|
+
|
|
2361
|
+
**Lifecycle Phases:**
|
|
2362
|
+
- `Pending` — provider created, not yet authenticated
|
|
2363
|
+
- `Authenticating` — auth flow in progress
|
|
2364
|
+
- `Discovering` — capability discovery running
|
|
2365
|
+
- `Ready` — provider fully operational
|
|
2366
|
+
- `Degraded` — partial functionality (some APIs failing)
|
|
2367
|
+
- `Failed` — provider unreachable or auth invalid
|
|
2368
|
+
|
|
2369
|
+
**Creation (via `createExternalBackendProvider()`):**
|
|
2370
|
+
- Validates: name required, providerType required
|
|
2371
|
+
- Initial status.phase: `Pending`
|
|
2372
|
+
- Labels and annotations initialized empty
|
|
2373
|
+
|
|
2374
|
+
**Provider Registry (`createDefaultProviderRegistry()`):**
|
|
2375
|
+
- Auto-registers GitHub adapter
|
|
2376
|
+
- GitHub descriptor exposes: gitForge, issueTracking, cicd interfaces
|
|
2377
|
+
- Factory methods: `createForge()`, `createIssueTracker()`, `createCicd()`
|
|
2378
|
+
|
|
2379
|
+
**Relationships:**
|
|
2380
|
+
- Belongs to: Organization
|
|
2381
|
+
- Has: ExternalBackendBinding (0:N)
|
|
2382
|
+
- Describes: ExternalProviderCapabilityManifest (1:1)
|
|
2383
|
+
- Authenticated via: GitHub App JWT + installation token exchange
|
|
2384
|
+
|
|
2385
|
+
---
|
|
2386
|
+
|
|
2387
|
+
#### ExternalBackendBinding
|
|
2388
|
+
|
|
2389
|
+
| Field | Value |
|
|
2390
|
+
|-------|-------|
|
|
2391
|
+
| Storage | etcd |
|
|
2392
|
+
| Context | external-backends |
|
|
2393
|
+
| Plural | externalbackendbindings |
|
|
2394
|
+
| Namespace | org-scoped |
|
|
2395
|
+
|
|
2396
|
+
**Purpose:** Binding of an external backend provider to an organization with credential
|
|
2397
|
+
reference and sync scope. Activates synchronization for specific resources.
|
|
2398
|
+
|
|
2399
|
+
**Required Spec Fields:**
|
|
2400
|
+
- `organizationRef` — owning organization
|
|
2401
|
+
- `providerRef` — reference to ExternalBackendProvider
|
|
2402
|
+
- `credentialRef` — reference to K8s Secret with provider credentials
|
|
2403
|
+
|
|
2404
|
+
**Optional Spec Fields:**
|
|
2405
|
+
- `scope` — what to sync: `{ repositories[], issues, pullRequests, pipelines }`
|
|
2406
|
+
- `webhookSecret` — shared secret for webhook HMAC verification
|
|
2407
|
+
- `syncEnabled` — boolean toggle
|
|
2408
|
+
|
|
2409
|
+
**Lifecycle Phases:**
|
|
2410
|
+
- `Pending` — binding created, not yet validated
|
|
2411
|
+
- `ValidatingTarget` — checking provider connectivity
|
|
2412
|
+
- `RegisteringWebhook` — setting up webhook on provider
|
|
2413
|
+
- `Backfilling` — importing existing data from provider
|
|
2414
|
+
- `Ready` — bidirectional sync active
|
|
2415
|
+
- `Degraded` — sync partially working
|
|
2416
|
+
- `Failed` — sync completely broken
|
|
2417
|
+
|
|
2418
|
+
**Relationships:**
|
|
2419
|
+
- Belongs to: Organization
|
|
2420
|
+
- References: ExternalBackendProvider
|
|
2421
|
+
- Controls: ExternalBackendSyncPolicy (sync behavior)
|
|
2422
|
+
- Produces: ExternalWebhookDelivery, ExternalSyncEvent, ExternalObjectLink
|
|
2423
|
+
- Manages: webhook registration on external provider
|
|
2424
|
+
|
|
2425
|
+
---
|
|
2426
|
+
|
|
2427
|
+
#### ExternalBackendSyncPolicy
|
|
2428
|
+
|
|
2429
|
+
| Field | Value |
|
|
2430
|
+
|-------|-------|
|
|
2431
|
+
| Storage | etcd |
|
|
2432
|
+
| Context | external-backends |
|
|
2433
|
+
| Plural | externalbackendsyncpolicies |
|
|
2434
|
+
| Namespace | org-scoped |
|
|
2435
|
+
|
|
2436
|
+
**Purpose:** Sync interval, conflict resolution mode, field mapping overrides, and retry
|
|
2437
|
+
policy for an external backend provider.
|
|
2438
|
+
|
|
2439
|
+
**Required Spec Fields:**
|
|
2440
|
+
- `organizationRef` — owning organization
|
|
2441
|
+
- `providerRef` — reference to ExternalBackendProvider
|
|
2442
|
+
- `syncInterval` — how often to poll for changes (e.g., '5m', '1h')
|
|
2443
|
+
|
|
2444
|
+
**Optional Spec Fields:**
|
|
2445
|
+
- `conflictResolution` — default strategy: `prefer-external`, `prefer-kradle`, `manual`
|
|
2446
|
+
- `fieldMappingOverrides` — custom field mappings
|
|
2447
|
+
- `retryPolicy` — `{ maxRetries, backoffMs }`
|
|
2448
|
+
- `webhookFirst` — boolean (prefer webhooks over polling)
|
|
2449
|
+
- `backfillInterval` — how often to do full backfill
|
|
2450
|
+
|
|
2451
|
+
**Ownership Modes (applied by sync-controller):**
|
|
2452
|
+
- `bidirectional` — both kradle and external may write
|
|
2453
|
+
- `external-owned` — external is authoritative; kradle is read-only
|
|
2454
|
+
- `kradle-owned` — kradle is authoritative; external is read-only
|
|
2455
|
+
|
|
2456
|
+
**Relationships:**
|
|
2457
|
+
- Belongs to: ExternalBackendProvider/Binding
|
|
2458
|
+
- Controls: sync-controller behavior
|
|
2459
|
+
- Determines: conflict resolution strategy
|
|
2460
|
+
|
|
2461
|
+
---
|
|
2462
|
+
|
|
2463
|
+
#### ExternalProviderCapabilityManifest
|
|
2464
|
+
|
|
2465
|
+
| Field | Value |
|
|
2466
|
+
|-------|-------|
|
|
2467
|
+
| Storage | etcd |
|
|
2468
|
+
| Context | external-backends |
|
|
2469
|
+
| Plural | externalprovidercapabilitymanifests |
|
|
2470
|
+
| Namespace | org-scoped |
|
|
2471
|
+
|
|
2472
|
+
**Purpose:** Discovered capability surface of an external backend provider including
|
|
2473
|
+
supported resource kinds and API features.
|
|
2474
|
+
|
|
2475
|
+
**Required Spec Fields:**
|
|
2476
|
+
- `organizationRef` — owning organization
|
|
2477
|
+
- `providerRef` — reference to ExternalBackendProvider
|
|
2478
|
+
- `capabilities` — capability description
|
|
2479
|
+
|
|
2480
|
+
**Capability Manifest Structure:**
|
|
2481
|
+
- `providerType` — provider type string
|
|
2482
|
+
- `interfaces[]` — implemented interfaces: `gitForge`, `issueTracking`, `cicd`
|
|
2483
|
+
|
|
2484
|
+
**Validation (`validateCapabilityManifest()`):**
|
|
2485
|
+
- providerType: required non-empty string
|
|
2486
|
+
- interfaces: non-empty array of known interface names
|
|
2487
|
+
- Unknown interfaces rejected
|
|
2488
|
+
|
|
2489
|
+
**Provider Adapter Validation (`validateProviderAdapter()`):**
|
|
2490
|
+
Required contract:
|
|
2491
|
+
- `descriptor()` → `{ providerType, displayName, hosting, authModes, apiCapabilities }`
|
|
2492
|
+
- `health()` → `{ status: 'healthy'|'degraded'|'unavailable', message }`
|
|
2493
|
+
- At least one interface: issueTracking, cicd, or gitForge
|
|
2494
|
+
- `normalizeWebhook(payload)` → NormalizedEvent[]
|
|
2495
|
+
- `verifyWebhook(request)` → `{ valid, reason }`
|
|
2496
|
+
|
|
2497
|
+
**Relationships:**
|
|
2498
|
+
- Belongs to: ExternalBackendProvider
|
|
2499
|
+
- Describes: what the provider can do
|
|
2500
|
+
- Used by: platform for routing sync operations to correct interface
|
|
2501
|
+
|
|
2502
|
+
---
|
|
2503
|
+
|
|
2504
|
+
#### ExternalWebhookDelivery
|
|
2505
|
+
|
|
2506
|
+
| Field | Value |
|
|
2507
|
+
|-------|-------|
|
|
2508
|
+
| Storage | postgres |
|
|
2509
|
+
| Context | external-backends |
|
|
2510
|
+
| Plural | externalwebhookdeliveries |
|
|
2511
|
+
| Namespace | org-scoped |
|
|
2512
|
+
|
|
2513
|
+
**Purpose:** Inbound webhook delivery from an external backend provider with event type,
|
|
2514
|
+
payload, and processing state.
|
|
2515
|
+
|
|
2516
|
+
**Required Spec Fields:**
|
|
2517
|
+
- `organizationRef` — owning organization
|
|
2518
|
+
- `providerRef` — which provider sent this
|
|
2519
|
+
- `eventType` — webhook event type (e.g., 'push', 'pull_request', 'issues')
|
|
2520
|
+
- `payload` — raw webhook payload
|
|
2521
|
+
|
|
2522
|
+
**Lifecycle Phases:**
|
|
2523
|
+
- `Received` — webhook received, signature verified
|
|
2524
|
+
- `Queued` — in processing queue
|
|
2525
|
+
- `Normalizing` — converting to canonical event format
|
|
2526
|
+
- `Processing` — being processed by sync controller
|
|
2527
|
+
- `Succeeded` — successfully processed
|
|
2528
|
+
- `DeadLettered` — processing failed after retries
|
|
2529
|
+
|
|
2530
|
+
**Webhook Processing (via webhook-controller):**
|
|
2531
|
+
1. Verify HMAC-SHA256 signature (timing-safe comparison)
|
|
2532
|
+
2. Check deduplication by deliveryId
|
|
2533
|
+
3. Create delivery record with timestamp
|
|
2534
|
+
4. Emit to subscriber queue
|
|
2535
|
+
5. Process: normalize event → upsert resources → update watermark
|
|
2536
|
+
|
|
2537
|
+
**HMAC Verification:**
|
|
2538
|
+
- Signature format: `sha256={hex digest}`
|
|
2539
|
+
- Uses `crypto.createHmac('sha256', secret)`
|
|
2540
|
+
- Timing-safe comparison via `crypto.timingSafeEqual()`
|
|
2541
|
+
- Rejects: missing signature, invalid format, mismatched HMAC
|
|
2542
|
+
|
|
2543
|
+
**Relationships:**
|
|
2544
|
+
- From: ExternalBackendProvider (via webhook)
|
|
2545
|
+
- Produces: ExternalSyncEvent (normalized)
|
|
2546
|
+
- Tracked by: ExternalSyncState (watermark)
|
|
2547
|
+
|
|
2548
|
+
---
|
|
2549
|
+
|
|
2550
|
+
#### ExternalSyncEvent
|
|
2551
|
+
|
|
2552
|
+
| Field | Value |
|
|
2553
|
+
|-------|-------|
|
|
2554
|
+
| Storage | postgres |
|
|
2555
|
+
| Context | external-backends |
|
|
2556
|
+
| Plural | externalsyncevents |
|
|
2557
|
+
| Namespace | org-scoped |
|
|
2558
|
+
|
|
2559
|
+
**Purpose:** Discrete sync event record from an external backend for a specific resource
|
|
2560
|
+
kind with deduplication and ordering metadata.
|
|
2561
|
+
|
|
2562
|
+
**Required Spec Fields:**
|
|
2563
|
+
- `organizationRef` — owning organization
|
|
2564
|
+
- `providerRef` — source provider
|
|
2565
|
+
- `eventKind` — event category
|
|
2566
|
+
- `resourceRef` — affected resource reference
|
|
2567
|
+
|
|
2568
|
+
**Event Normalization (via sync-controller `normalizeEvent()`):**
|
|
2569
|
+
Input:
|
|
2570
|
+
```
|
|
2571
|
+
{ eventType, action, nativeId, providerRef, resourceKind, data, receivedAt }
|
|
2572
|
+
```
|
|
2573
|
+
Output (canonical format):
|
|
2574
|
+
```
|
|
2575
|
+
{ eventType, action, nativeId, providerRef, resourceKind, data, receivedAt, canonicalAt }
|
|
2576
|
+
```
|
|
2577
|
+
|
|
2578
|
+
**Relationships:**
|
|
2579
|
+
- Produced by: ExternalWebhookDelivery processing
|
|
2580
|
+
- Updates: ExternalSyncState (watermark)
|
|
2581
|
+
- May produce: ExternalSyncConflict (on field divergence)
|
|
2582
|
+
|
|
2583
|
+
---
|
|
2584
|
+
|
|
2585
|
+
#### ExternalSyncState
|
|
2586
|
+
|
|
2587
|
+
| Field | Value |
|
|
2588
|
+
|-------|-------|
|
|
2589
|
+
| Storage | postgres |
|
|
2590
|
+
| Context | external-backends |
|
|
2591
|
+
| Plural | externalsyncstates |
|
|
2592
|
+
| Namespace | org-scoped |
|
|
2593
|
+
|
|
2594
|
+
**Purpose:** Current sync phase, last successful sync timestamp, and error details for
|
|
2595
|
+
an external resource binding. Implements high-watermark tracking.
|
|
2596
|
+
|
|
2597
|
+
**Required Spec Fields:**
|
|
2598
|
+
- `organizationRef` — owning organization
|
|
2599
|
+
- `providerRef` — source provider
|
|
2600
|
+
- `resourceRef` — the resource being tracked
|
|
2601
|
+
- `phase` — current sync phase
|
|
2602
|
+
|
|
2603
|
+
**Watermark Tracking (via sync-controller):**
|
|
2604
|
+
- `updateWatermark(bindingRef, timestamp)` — advances if newer than current
|
|
2605
|
+
- `getWatermark(bindingRef)` — returns current watermark or null
|
|
2606
|
+
- Persisted as CRD-shaped resource with bindingRef and timestamp
|
|
2607
|
+
|
|
2608
|
+
**Relationships:**
|
|
2609
|
+
- Tracks: sync progress for each binding/resource pair
|
|
2610
|
+
- Updated by: sync-controller after successful event processing
|
|
2611
|
+
- Used for: resume-from-last-known-good on reconnection
|
|
2612
|
+
|
|
2613
|
+
---
|
|
2614
|
+
|
|
2615
|
+
#### ExternalWriteIntent
|
|
2616
|
+
|
|
2617
|
+
| Field | Value |
|
|
2618
|
+
|-------|-------|
|
|
2619
|
+
| Storage | postgres |
|
|
2620
|
+
| Context | external-backends |
|
|
2621
|
+
| Plural | externalwriteintents |
|
|
2622
|
+
| Namespace | org-scoped |
|
|
2623
|
+
|
|
2624
|
+
**Purpose:** Queued write-back intent to an external backend with operation, payload
|
|
2625
|
+
snapshot, and approval state. Manages the lifecycle of outbound mutations.
|
|
2626
|
+
|
|
2627
|
+
**Required Spec Fields:**
|
|
2628
|
+
- `organizationRef` — owning organization
|
|
2629
|
+
- `providerRef` — target provider
|
|
2630
|
+
- `resourceRef` — resource being written
|
|
2631
|
+
- `operation` — operation type (e.g., 'create', 'update', 'delete')
|
|
2632
|
+
|
|
2633
|
+
**Optional Spec Fields:**
|
|
2634
|
+
- `interfaceKey` — which interface to use (gitForge, issueTracking, cicd)
|
|
2635
|
+
- `payload` — operation payload
|
|
2636
|
+
- `requiresApproval` — boolean
|
|
2637
|
+
- `maxRetries` — max retry attempts (default: 3)
|
|
2638
|
+
- `idempotencyKey` — deterministic key for dedup
|
|
2639
|
+
|
|
2640
|
+
**Lifecycle Phases:**
|
|
2641
|
+
- `PendingApproval` — write requires human approval
|
|
2642
|
+
- `ReadyToSend` — approved (or no approval needed), ready for execution
|
|
2643
|
+
- `Sending` — write in progress
|
|
2644
|
+
- `Retrying` — write failed, retrying
|
|
2645
|
+
- `Succeeded` — write completed successfully
|
|
2646
|
+
- `Failed` — write failed after all retries exhausted
|
|
2647
|
+
- `Rejected` — write approval denied
|
|
2648
|
+
|
|
2649
|
+
**Idempotency Key Generation (`getIdempotencyKey()`):**
|
|
2650
|
+
- Deterministic hash of: interfaceKey + operation + resourceRef + payload
|
|
2651
|
+
- Format: `idem-{interfaceKey}-{operation}-{hash}`
|
|
2652
|
+
- Prevents duplicate writes for identical operations
|
|
2653
|
+
|
|
2654
|
+
**Operations:**
|
|
2655
|
+
- `createWriteIntent()` — creates with validation and idempotency key
|
|
2656
|
+
- `approveWriteIntent()` — PendingApproval → ReadyToSend
|
|
2657
|
+
- `rejectWriteIntent()` — PendingApproval → Rejected
|
|
2658
|
+
- `executeWriteIntent()` — ReadyToSend → Sending → Succeeded/Retrying/Failed
|
|
2659
|
+
|
|
2660
|
+
**Execution (`executeWriteIntent()`):**
|
|
2661
|
+
1. Verify phase is ReadyToSend
|
|
2662
|
+
2. Transition to Sending
|
|
2663
|
+
3. Call executor function
|
|
2664
|
+
4. On success: → Succeeded (with externalResult)
|
|
2665
|
+
5. On failure: increment retry, → Retrying (if retries remain)
|
|
2666
|
+
6. On exhaustion: → Failed (with lastError)
|
|
2667
|
+
|
|
2668
|
+
**Relationships:**
|
|
2669
|
+
- Targets: ExternalBackendProvider (via providerRef)
|
|
2670
|
+
- May require: AgentApproval (if requiresApproval)
|
|
2671
|
+
- May produce: ExternalSyncConflict (on conflict during write)
|
|
2672
|
+
|
|
2673
|
+
---
|
|
2674
|
+
|
|
2675
|
+
#### ExternalSyncConflict
|
|
2676
|
+
|
|
2677
|
+
| Field | Value |
|
|
2678
|
+
|-------|-------|
|
|
2679
|
+
| Storage | postgres |
|
|
2680
|
+
| Context | external-backends |
|
|
2681
|
+
| Plural | externalsyncconflicts |
|
|
2682
|
+
| Namespace | org-scoped |
|
|
2683
|
+
|
|
2684
|
+
**Purpose:** Detected conflict between local Kradle state and external provider state
|
|
2685
|
+
with conflict kind, field-level diff, and resolution outcome.
|
|
2686
|
+
|
|
2687
|
+
**Required Spec Fields:**
|
|
2688
|
+
- `organizationRef` — owning organization
|
|
2689
|
+
- `providerRef` — provider where conflict was detected
|
|
2690
|
+
- `resourceRef` — affected resource
|
|
2691
|
+
- `conflictKind` — type of conflict
|
|
2692
|
+
|
|
2693
|
+
**Optional Spec Fields:**
|
|
2694
|
+
- `fieldPath` — specific field in conflict
|
|
2695
|
+
- `localValue` — Kradle's value for the field
|
|
2696
|
+
- `externalValue` — external provider's value
|
|
2697
|
+
- `detectedAt` — when conflict was found
|
|
2698
|
+
|
|
2699
|
+
**Lifecycle Phases:**
|
|
2700
|
+
- `Open` — conflict detected, unresolved
|
|
2701
|
+
- `Resolving` — resolution in progress
|
|
2702
|
+
- `Resolved` — conflict resolved (with chosen value)
|
|
2703
|
+
- `Ignored` — conflict intentionally ignored
|
|
2704
|
+
- `Superseded` — new sync event made this conflict irrelevant
|
|
2705
|
+
|
|
2706
|
+
**Resolution Strategies:**
|
|
2707
|
+
- `prefer-external` — use external provider's value
|
|
2708
|
+
- `prefer-kradle` — use Kradle's value
|
|
2709
|
+
- `manual` — use explicitly provided resolvedValue
|
|
2710
|
+
- `ignore` — mark as Ignored, no value chosen
|
|
2711
|
+
|
|
2712
|
+
**Operations:**
|
|
2713
|
+
- `detectConflict()` — creates conflict if localValue !== externalValue
|
|
2714
|
+
- `resolveConflict()` — applies strategy, transitions to Resolved/Ignored
|
|
2715
|
+
- `supersededCheck()` — marks all Open conflicts for a resource/field as Superseded
|
|
2716
|
+
- `getOpenConflicts()` — lists all Open (unresolved) conflicts
|
|
2717
|
+
|
|
2718
|
+
**Relationships:**
|
|
2719
|
+
- References: ExternalBackendProvider, affected resource
|
|
2720
|
+
- Resolved by: human operator or automated policy
|
|
2721
|
+
- May block: ExternalWriteIntent (if unresolved)
|
|
2722
|
+
|
|
2723
|
+
---
|
|
2724
|
+
|
|
2725
|
+
#### ExternalObjectLink
|
|
2726
|
+
|
|
2727
|
+
| Field | Value |
|
|
2728
|
+
|-------|-------|
|
|
2729
|
+
| Storage | postgres |
|
|
2730
|
+
| Context | external-backends |
|
|
2731
|
+
| Plural | externalobjectlinks |
|
|
2732
|
+
| Namespace | org-scoped |
|
|
2733
|
+
|
|
2734
|
+
**Purpose:** Stable mapping between a Kradle local resource and its external backend
|
|
2735
|
+
counterpart. The identity envelope that tracks external IDs, URLs, and ETags.
|
|
2736
|
+
|
|
2737
|
+
**Required Spec Fields:**
|
|
2738
|
+
- `organizationRef` — owning organization
|
|
2739
|
+
- `providerRef` — external provider
|
|
2740
|
+
- `externalId` — native ID on the external system (e.g., GitHub node ID)
|
|
2741
|
+
- `localRef` — reference to local Kradle resource
|
|
2742
|
+
|
|
2743
|
+
**Optional Spec Fields (in status.external on synced resources):**
|
|
2744
|
+
- `nativeId` — external system's identifier
|
|
2745
|
+
- `url` — external URL (e.g., GitHub PR URL)
|
|
2746
|
+
- `etag` — HTTP ETag for change detection
|
|
2747
|
+
- `lastSyncedAt` — last successful sync timestamp
|
|
2748
|
+
- `firstSyncedAt` — when first synced
|
|
2749
|
+
|
|
2750
|
+
**Upsert Behavior (via sync-controller `upsertResource()`):**
|
|
2751
|
+
- Creates resource with external envelope in status
|
|
2752
|
+
- Preserves `firstSyncedAt` from existing record
|
|
2753
|
+
- Updates `lastSyncedAt` to current time
|
|
2754
|
+
- Sets phase to `Synced`
|
|
2755
|
+
|
|
2756
|
+
**Relationships:**
|
|
2757
|
+
- Links: local Kradle resource ↔ external resource
|
|
2758
|
+
- Used by: sync controller for bidirectional mapping
|
|
2759
|
+
- Enables: URL resolution, change detection (ETag), dedup
|
|
2760
|
+
|
|
2761
|
+
---
|
|
2762
|
+
|
|
2763
|
+
### 1.9 Control Plane Domain (3 kinds)
|
|
2764
|
+
|
|
2765
|
+
---
|
|
2766
|
+
|
|
2767
|
+
#### PullRequest
|
|
2768
|
+
|
|
2769
|
+
| Field | Value |
|
|
2770
|
+
|-------|-------|
|
|
2771
|
+
| Storage | postgres |
|
|
2772
|
+
| Context | control-plane |
|
|
2773
|
+
| Plural | pullrequests |
|
|
2774
|
+
| Namespace | org-scoped |
|
|
2775
|
+
|
|
2776
|
+
**Purpose:** Review unit with source/target refs, title, checks, and merge lifecycle.
|
|
2777
|
+
|
|
2778
|
+
**Required Spec Fields:**
|
|
2779
|
+
- `organizationRef` — owning organization
|
|
2780
|
+
- `repository` — target repository
|
|
2781
|
+
- `title` — PR title
|
|
2782
|
+
|
|
2783
|
+
**Gitea Integration:**
|
|
2784
|
+
- `createGiteaBackend().createPullRequest({ owner, repo, title, head, base, body })`
|
|
2785
|
+
|
|
2786
|
+
**GitHub Integration:**
|
|
2787
|
+
- `GitHubGitForge.createPullRequest({ repo, title, head, base, body })`
|
|
2788
|
+
- `GitHubGitForge.getPullRequest({ repo, pullNumber })`
|
|
2789
|
+
- `GitHubGitForge.mergePullRequest({ repo, pullNumber, mergeMethod })`
|
|
2790
|
+
- Normalized: `{ number, title, state, head, base, body, merged, htmlUrl }`
|
|
2791
|
+
|
|
2792
|
+
**Relationships:**
|
|
2793
|
+
- Belongs to: Organization, Repository
|
|
2794
|
+
- Has: Review (0:N)
|
|
2795
|
+
- Linked via: WorkItemSessionLink (to agent sessions)
|
|
2796
|
+
- Synced from: GitHub PRs via ExternalBackendBinding
|
|
2797
|
+
|
|
2798
|
+
---
|
|
2799
|
+
|
|
2800
|
+
#### Issue
|
|
2801
|
+
|
|
2802
|
+
| Field | Value |
|
|
2803
|
+
|-------|-------|
|
|
2804
|
+
| Storage | postgres |
|
|
2805
|
+
| Context | control-plane |
|
|
2806
|
+
| Plural | issues |
|
|
2807
|
+
| Namespace | org-scoped |
|
|
2808
|
+
|
|
2809
|
+
**Purpose:** Project-scoped work item with labels, comments, backend sync metadata,
|
|
2810
|
+
and zero-or-more repository associations.
|
|
2811
|
+
|
|
2812
|
+
**Required Spec Fields:**
|
|
2813
|
+
- `organizationRef` — owning organization
|
|
2814
|
+
- `title` — issue title
|
|
2815
|
+
|
|
2816
|
+
**Optional Spec Fields:**
|
|
2817
|
+
- `repositoryRefs[]` — associated repositories
|
|
2818
|
+
- `projectRefs[]` — associated projects
|
|
2819
|
+
- `workflowState` — kanban column ID
|
|
2820
|
+
- `labels[]` — issue labels
|
|
2821
|
+
- `assignees[]` — assigned users
|
|
2822
|
+
|
|
2823
|
+
**Gitea Integration:**
|
|
2824
|
+
- `createGiteaBackend().createIssue({ owner, repo, title, body, labels, assignees })`
|
|
2825
|
+
- `giteaIssueSyncPlan()` — plans: ensureOrgMemoryRepository, syncIssue, writeIssueRepositoryMetadata
|
|
2826
|
+
|
|
2827
|
+
**GitHub Integration:**
|
|
2828
|
+
- `GitHubIssueTracking.listIssues({ repo, state })`
|
|
2829
|
+
- `GitHubIssueTracking.createIssue({ repo, title, body, labels })`
|
|
2830
|
+
- `GitHubIssueTracking.updateIssue({ repo, issueNumber, title, body, labels })`
|
|
2831
|
+
- `GitHubIssueTracking.closeIssue({ repo, issueNumber })`
|
|
2832
|
+
- Normalized: `{ id, number, title, state, body, labels, author, htmlUrl }`
|
|
2833
|
+
- `githubProjectIssueSyncPlan()` — plans: syncProjectItem, syncIssueMetadata, syncRepositoryLinks
|
|
2834
|
+
|
|
2835
|
+
**Project Relationship:**
|
|
2836
|
+
- Issues belong to KradleProjects via `projectRefs`
|
|
2837
|
+
- `workflowState` corresponds to project's `workflowColumns[].id`
|
|
2838
|
+
- Kanban drag-drop updates workflowState
|
|
2839
|
+
- Board derives columns from `project.spec.workflowColumns`
|
|
2840
|
+
|
|
2841
|
+
**Relationships:**
|
|
2842
|
+
- Belongs to: Organization
|
|
2843
|
+
- References: Repository (0:N via repositoryRefs), KradleProject (0:N via projectRefs)
|
|
2844
|
+
- Has: Comments (via GitHub/Gitea sync)
|
|
2845
|
+
- Linked via: WorkItemSessionLink, WorkItemWorkspaceLink
|
|
2846
|
+
- Synced from: GitHub Issues, Gitea Issues
|
|
2847
|
+
|
|
2848
|
+
---
|
|
2849
|
+
|
|
2850
|
+
#### Review
|
|
2851
|
+
|
|
2852
|
+
| Field | Value |
|
|
2853
|
+
|-------|-------|
|
|
2854
|
+
| Storage | postgres |
|
|
2855
|
+
| Context | control-plane |
|
|
2856
|
+
| Plural | reviews |
|
|
2857
|
+
| Namespace | org-scoped |
|
|
2858
|
+
|
|
2859
|
+
**Purpose:** Approval, comment, or change-request record for a pull request.
|
|
2860
|
+
|
|
2861
|
+
**Required Spec Fields:**
|
|
2862
|
+
- `organizationRef` — owning organization
|
|
2863
|
+
- `pullRequest` — reference to the PullRequest
|
|
2864
|
+
|
|
2865
|
+
**Optional Spec Fields:**
|
|
2866
|
+
- `state` — `approved`, `changes_requested`, `commented`
|
|
2867
|
+
- `body` — review body text
|
|
2868
|
+
- `author` — reviewer username
|
|
2869
|
+
|
|
2870
|
+
**Relationships:**
|
|
2871
|
+
- Belongs to: PullRequest
|
|
2872
|
+
- Synced from: GitHub PR reviews
|
|
2873
|
+
|
|
2874
|
+
---
|
|
2875
|
+
|
|
2876
|
+
### 1.10 CI/CD Domain (3 kinds)
|
|
2877
|
+
|
|
2878
|
+
---
|
|
2879
|
+
|
|
2880
|
+
#### Pipeline
|
|
2881
|
+
|
|
2882
|
+
| Field | Value |
|
|
2883
|
+
|-------|-------|
|
|
2884
|
+
| Storage | postgres |
|
|
2885
|
+
| Context | runners-ci |
|
|
2886
|
+
| Plural | pipelines |
|
|
2887
|
+
| Namespace | org-scoped |
|
|
2888
|
+
|
|
2889
|
+
**Purpose:** CI pipeline run state with trust tier, steps, current step, and resume point.
|
|
2890
|
+
|
|
2891
|
+
**Required Spec Fields:**
|
|
2892
|
+
- `organizationRef` — owning organization
|
|
2893
|
+
- `repository` — target repository
|
|
2894
|
+
- `ref` — git ref (branch, tag, commit)
|
|
2895
|
+
|
|
2896
|
+
**Optional Spec Fields:**
|
|
2897
|
+
- `actor` — who triggered the pipeline
|
|
2898
|
+
- `steps[]` — ordered step names (e.g., ['checkout', 'test', 'build'])
|
|
2899
|
+
- `trustTier` — `trusted` or `untrusted` (fork = untrusted)
|
|
2900
|
+
- `resumeFrom` — step to resume from (for reruns)
|
|
2901
|
+
|
|
2902
|
+
**Creation (via `RunnerScheduler.startPipeline()`):**
|
|
2903
|
+
- Creates Pipeline resource with phase: Running
|
|
2904
|
+
- Creates Job resource for each step
|
|
2905
|
+
- First job (or resumeFrom) starts as Running, others as Pending
|
|
2906
|
+
- Trust tier from fork status (fork = untrusted)
|
|
2907
|
+
|
|
2908
|
+
**Rerun:**
|
|
2909
|
+
- `RunnerScheduler.rerunFromStep()` — creates new pipeline with resumeFrom
|
|
2910
|
+
|
|
2911
|
+
**GitHub Integration:**
|
|
2912
|
+
- `GitHubCicd.listWorkflowRuns({ repo, workflowId })`
|
|
2913
|
+
- Normalized: `{ id, name, status, conclusion, headBranch, headSha, htmlUrl, createdAt }`
|
|
2914
|
+
- External pipeline events via webhook → ExternalWebhookDelivery → Pipeline projection
|
|
2915
|
+
|
|
2916
|
+
**Relationships:**
|
|
2917
|
+
- Belongs to: Organization, Repository
|
|
2918
|
+
- Has: Job (1:N, one per step)
|
|
2919
|
+
- Scheduled on: RunnerPool
|
|
2920
|
+
- Synced from: GitHub Actions workflow_run events
|
|
2921
|
+
|
|
2922
|
+
---
|
|
2923
|
+
|
|
2924
|
+
#### Job
|
|
2925
|
+
|
|
2926
|
+
| Field | Value |
|
|
2927
|
+
|-------|-------|
|
|
2928
|
+
| Storage | postgres |
|
|
2929
|
+
| Context | runners-ci |
|
|
2930
|
+
| Plural | jobs |
|
|
2931
|
+
| Namespace | org-scoped |
|
|
2932
|
+
|
|
2933
|
+
**Purpose:** Executable CI step with service-account scope and isolation metadata.
|
|
2934
|
+
|
|
2935
|
+
**Required Spec Fields:**
|
|
2936
|
+
- `organizationRef` — owning organization
|
|
2937
|
+
- `pipeline` — reference to parent Pipeline
|
|
2938
|
+
- `step` — step name
|
|
2939
|
+
|
|
2940
|
+
**Optional Spec Fields:**
|
|
2941
|
+
- `serviceAccount` — computed service account name for RBAC isolation
|
|
2942
|
+
|
|
2943
|
+
**Service Account Generation (`serviceAccountForJob()`):**
|
|
2944
|
+
- Format: `kradle-runner-{namespace}-{repository}-{pipeline}-{trustTier}`
|
|
2945
|
+
- Ensures each job runs with appropriate permissions
|
|
2946
|
+
|
|
2947
|
+
**Lifecycle Phases:**
|
|
2948
|
+
- `Pending` — waiting for runner assignment
|
|
2949
|
+
- `Running` — executing on a runner
|
|
2950
|
+
- `Succeeded` — completed successfully
|
|
2951
|
+
- `Failed` — execution failed
|
|
2952
|
+
|
|
2953
|
+
**GitHub Integration:**
|
|
2954
|
+
- `GitHubCicd.listJobs({ repo, runId })`
|
|
2955
|
+
- Normalized: `{ id, name, status, conclusion, startedAt, completedAt, htmlUrl }`
|
|
2956
|
+
|
|
2957
|
+
**Runner Assignment:**
|
|
2958
|
+
- `runnerController.scheduleJob(pool, job)` — assigns job to idle runner or creates new
|
|
2959
|
+
- Volume: workspace PVC mounted at /workspace
|
|
2960
|
+
- Environment: KRADLE_ORG, KRADLE_RUN_ID, KRADLE_WORKSPACE_PATH
|
|
2961
|
+
|
|
2962
|
+
**Relationships:**
|
|
2963
|
+
- Belongs to: Pipeline
|
|
2964
|
+
- Scheduled on: RunnerPool runner
|
|
2965
|
+
- Has: service account for RBAC isolation
|
|
2966
|
+
|
|
2967
|
+
---
|
|
2968
|
+
|
|
2969
|
+
#### WebhookDelivery (outbound)
|
|
2970
|
+
|
|
2971
|
+
| Field | Value |
|
|
2972
|
+
|-------|-------|
|
|
2973
|
+
| Storage | postgres |
|
|
2974
|
+
| Context | hooks-events |
|
|
2975
|
+
| Plural | webhookdeliveries |
|
|
2976
|
+
| Namespace | org-scoped |
|
|
2977
|
+
|
|
2978
|
+
**Purpose:** Durable outbound webhook delivery attempt with signature, phase, response,
|
|
2979
|
+
and replay metadata.
|
|
2980
|
+
|
|
2981
|
+
**Required Spec Fields:**
|
|
2982
|
+
- `organizationRef` — owning organization
|
|
2983
|
+
- `subscription` — reference to WebhookSubscription
|
|
2984
|
+
- `eventType` — event type being delivered
|
|
2985
|
+
- `signature` — HMAC signature of payload
|
|
2986
|
+
|
|
2987
|
+
**Lifecycle Phases:**
|
|
2988
|
+
- `Pending` — queued for delivery
|
|
2989
|
+
- `Delivering` — HTTP request in flight
|
|
2990
|
+
- `Delivered` — 2xx response received
|
|
2991
|
+
- `Failed` — non-2xx response or network error
|
|
2992
|
+
- `Retrying` — failed, will retry
|
|
2993
|
+
|
|
2994
|
+
**Relationships:**
|
|
2995
|
+
- Belongs to: WebhookSubscription
|
|
2996
|
+
- Contains: delivery attempt history
|
|
2997
|
+
- Triggered by: resource-change events via event bus
|
|
2998
|
+
|
|
2999
|
+
---
|
|
3000
|
+
|
|
3001
|
+
## PART 2: External Backend Relationships
|
|
3002
|
+
|
|
3003
|
+
---
|
|
3004
|
+
|
|
3005
|
+
### 2.1 Gitea Integration
|
|
3006
|
+
|
|
3007
|
+
Gitea serves as the default git hosting backend. The integration is implemented in
|
|
3008
|
+
`gitea-backend.js` and `gitea-service.js`.
|
|
3009
|
+
|
|
3010
|
+
#### Repository ↔ Gitea Repo
|
|
3011
|
+
|
|
3012
|
+
| Kradle Operation | Gitea API Call |
|
|
3013
|
+
|-----------------|----------------|
|
|
3014
|
+
| Create Repository | `POST /api/v1/orgs/{owner}/repos` (org) or `POST /api/v1/user/repos` (personal) |
|
|
3015
|
+
| Private flag | Derived from visibility: private/internal → `private: true`, public → `private: false` |
|
|
3016
|
+
| Default branch | `default_branch` parameter |
|
|
3017
|
+
| Auto-init | `auto_init: false` (repo initialized externally) |
|
|
3018
|
+
|
|
3019
|
+
#### SSHKey ↔ Gitea Deploy Keys
|
|
3020
|
+
|
|
3021
|
+
| Kradle Operation | Gitea API Call |
|
|
3022
|
+
|-----------------|----------------|
|
|
3023
|
+
| Add deploy key | `POST /api/v1/repos/{owner}/{repo}/keys` with `{ title, key, read_only }` |
|
|
3024
|
+
| Add user key | `POST /api/v1/user/keys` with `{ title, key, read_only }` |
|
|
3025
|
+
| Remove key | (not exposed in current backend — reconciler handles) |
|
|
3026
|
+
|
|
3027
|
+
#### RepositoryPermission ↔ Gitea Collaborators
|
|
3028
|
+
|
|
3029
|
+
| Kradle Operation | Gitea API Call |
|
|
3030
|
+
|-----------------|----------------|
|
|
3031
|
+
| Add collaborator | `PUT /api/v1/repos/{owner}/{repo}/collaborators/{username}` with `{ permission }` |
|
|
3032
|
+
| Team repository | `PUT /api/v1/teams/{team}/repos/{owner}/{repo}` with `{ permission }` |
|
|
3033
|
+
| Permission levels | `read`, `write`, `admin` (direct mapping) |
|
|
3034
|
+
|
|
3035
|
+
#### Team ↔ Gitea Teams
|
|
3036
|
+
|
|
3037
|
+
| Kradle Operation | Gitea API Call |
|
|
3038
|
+
|-----------------|----------------|
|
|
3039
|
+
| Create team | `POST /api/v1/orgs/{org}/teams` with `{ name, permission, units }` |
|
|
3040
|
+
| Add member | `PUT /api/v1/teams/{team}/members/{username}` |
|
|
3041
|
+
| Default units | `['repo.code', 'repo.pulls', 'repo.issues']` |
|
|
3042
|
+
|
|
3043
|
+
#### BranchProtection ↔ Gitea Branch Protection
|
|
3044
|
+
|
|
3045
|
+
| Kradle Operation | Gitea API Call |
|
|
3046
|
+
|-----------------|----------------|
|
|
3047
|
+
| Protect branch | `POST /api/v1/repos/{owner}/{repo}/branch_protections` |
|
|
3048
|
+
| Parameters | `branch_name`, `enable_push: false`, `enable_push_whitelist: true`, `required_approvals`, status checks |
|
|
3049
|
+
|
|
3050
|
+
#### Issue/PR ↔ Gitea
|
|
3051
|
+
|
|
3052
|
+
| Kradle Operation | Gitea API Call |
|
|
3053
|
+
|-----------------|----------------|
|
|
3054
|
+
| Create issue | `POST /api/v1/repos/{owner}/{repo}/issues` |
|
|
3055
|
+
| Create PR | `POST /api/v1/repos/{owner}/{repo}/pulls` |
|
|
3056
|
+
| Create webhook | `POST /api/v1/repos/{owner}/{repo}/hooks` (type: 'gitea') |
|
|
3057
|
+
|
|
3058
|
+
#### Git Tree/Blob API (gitea-service.js)
|
|
3059
|
+
|
|
3060
|
+
| Operation | Gitea API Call |
|
|
3061
|
+
|-----------|----------------|
|
|
3062
|
+
| listTree | `GET /api/v1/repos/{owner}/{repo}/contents/{path}?ref={ref}` |
|
|
3063
|
+
| getBlob | `GET /api/v1/repos/{owner}/{repo}/raw/{filepath}?ref={ref}` |
|
|
3064
|
+
| listBranches | `GET /api/v1/repos/{owner}/{repo}/branches` |
|
|
3065
|
+
|
|
3066
|
+
**Fallback Behavior:**
|
|
3067
|
+
- `createGiteaService()` returns `null` when `KRADLE_GITEA_HTTP_URL` is not set
|
|
3068
|
+
- Callers fall back to mock data when service is null
|
|
3069
|
+
- 404 responses return null (graceful degradation)
|
|
3070
|
+
|
|
3071
|
+
#### Repository Integration Plan (`giteaRepositoryIntegrationPlan()`)
|
|
3072
|
+
|
|
3073
|
+
Complete integration requires these sequential operations:
|
|
3074
|
+
1. `createOrganization` — ensure Gitea org exists
|
|
3075
|
+
2. `createRepository` — create the repo
|
|
3076
|
+
3. `ensureUserMappings` — map Kradle users to Gitea users
|
|
3077
|
+
4. `addDeployKey` — add GitOps deploy key (read/write)
|
|
3078
|
+
5. `addUserSshKey` — add developer keys
|
|
3079
|
+
6. `addCollaborator` — set permissions
|
|
3080
|
+
7. `addTeamRepository` — grant team access (maintainers: admin)
|
|
3081
|
+
8. `protectBranch` — protect main branch
|
|
3082
|
+
9. `createWebhook` — register event webhook
|
|
3083
|
+
|
|
3084
|
+
#### Issue Sync Plan (`giteaIssueSyncPlan()`)
|
|
3085
|
+
|
|
3086
|
+
For issue synchronization with Gitea:
|
|
3087
|
+
1. `ensureOrgMemoryRepository` — ensure `_${org}_` repo exists
|
|
3088
|
+
2. `syncIssue` — create/update issue in Gitea
|
|
3089
|
+
3. `writeIssueRepositoryMetadata` — write metadata labels linking issue to repositories
|
|
3090
|
+
|
|
3091
|
+
---
|
|
3092
|
+
|
|
3093
|
+
### 2.2 GitHub Integration (External Backend)
|
|
3094
|
+
|
|
3095
|
+
GitHub is implemented as the first ExternalBackendProvider with full adapter support.
|
|
3096
|
+
|
|
3097
|
+
#### Authentication Flow
|
|
3098
|
+
|
|
3099
|
+
1. **JWT Creation (`createGitHubJwt()`):**
|
|
3100
|
+
- Encodes: `{ iat, exp, iss: appId }` with RS256 (production) or HS256 (test)
|
|
3101
|
+
- PEM key detection: looks for `-----BEGIN` prefix
|
|
3102
|
+
- Produces: signed JWT for GitHub App authentication
|
|
3103
|
+
|
|
3104
|
+
2. **Installation Token Exchange (`exchangeInstallationToken()`):**
|
|
3105
|
+
- Endpoint: `POST /app/installations/{installationId}/access_tokens`
|
|
3106
|
+
- Authorization: `Bearer {appJwt}`
|
|
3107
|
+
- Returns: `{ token, expiresAt }`
|
|
3108
|
+
- Token used for all subsequent API calls
|
|
3109
|
+
|
|
3110
|
+
#### Git Forge Interface (GitHubGitForge)
|
|
3111
|
+
|
|
3112
|
+
| Method | GitHub API | Returns |
|
|
3113
|
+
|--------|-----------|---------|
|
|
3114
|
+
| `listRepositories()` | `GET /installation/repositories` | `NormalizedRepo[]` |
|
|
3115
|
+
| `getPullRequest({ repo, pullNumber })` | `GET /repos/{owner}/{repo}/pulls/{number}` | `NormalizedPR` |
|
|
3116
|
+
| `createPullRequest({ repo, title, head, base, body })` | `POST /repos/{owner}/{repo}/pulls` | `NormalizedPR` |
|
|
3117
|
+
| `mergePullRequest({ repo, pullNumber, mergeMethod })` | `PUT /repos/{owner}/{repo}/pulls/{number}/merge` | `{ merged, sha, message }` |
|
|
3118
|
+
| `listRefs({ repo })` | `GET /repos/.../branches` + `GET /repos/.../tags` | `{ branches, tags }` |
|
|
3119
|
+
| `syncDeployKeys({ repo, desiredKeys })` | GET+DELETE+POST `/repos/.../keys` | `{ added, removed }` |
|
|
3120
|
+
| `syncBranchProtection({ repo, branch, ... })` | `PUT /repos/.../branches/{branch}/protection` | protection object |
|
|
3121
|
+
|
|
3122
|
+
#### Issue Tracking Interface (GitHubIssueTracking)
|
|
3123
|
+
|
|
3124
|
+
| Method | GitHub API | Returns |
|
|
3125
|
+
|--------|-----------|---------|
|
|
3126
|
+
| `listIssues({ repo, state })` | `GET /repos/{owner}/{repo}/issues?state={state}` | `NormalizedIssue[]` |
|
|
3127
|
+
| `createIssue({ repo, title, body, labels })` | `POST /repos/{owner}/{repo}/issues` | `NormalizedIssue` |
|
|
3128
|
+
| `updateIssue({ repo, issueNumber, ... })` | `PATCH /repos/{owner}/{repo}/issues/{number}` | `NormalizedIssue` |
|
|
3129
|
+
| `closeIssue({ repo, issueNumber })` | `PATCH .../issues/{number}` (state: closed) | `NormalizedIssue` |
|
|
3130
|
+
| `listComments({ repo, issueNumber })` | `GET /repos/.../issues/{number}/comments` | `NormalizedComment[]` |
|
|
3131
|
+
| `createComment({ repo, issueNumber, body })` | `POST /repos/.../issues/{number}/comments` | `NormalizedComment` |
|
|
3132
|
+
|
|
3133
|
+
#### CI/CD Interface (GitHubCicd)
|
|
3134
|
+
|
|
3135
|
+
| Method | GitHub API | Returns |
|
|
3136
|
+
|--------|-----------|---------|
|
|
3137
|
+
| `listWorkflowRuns({ repo, workflowId })` | `GET /repos/.../actions/runs` or `.../workflows/{id}/runs` | `NormalizedWorkflowRun[]` |
|
|
3138
|
+
| `listJobs({ repo, runId })` | `GET /repos/.../actions/runs/{id}/jobs` | `NormalizedJob[]` |
|
|
3139
|
+
| `rerunWorkflow({ repo, runId })` | `POST /repos/.../actions/runs/{id}/rerun` | `{ triggered, runId }` |
|
|
3140
|
+
| `cancelWorkflow({ repo, runId })` | `POST /repos/.../actions/runs/{id}/cancel` | `{ cancelled, runId }` |
|
|
3141
|
+
| `createCheck({ repo, name, headSha, ... })` | `POST /repos/.../check-runs` | `NormalizedCheckRun` |
|
|
3142
|
+
| `updateCheck({ repo, checkRunId, ... })` | `PATCH /repos/.../check-runs/{id}` | `NormalizedCheckRun` |
|
|
3143
|
+
|
|
3144
|
+
#### Webhook Events Handled
|
|
3145
|
+
|
|
3146
|
+
The webhook controller processes these GitHub event types:
|
|
3147
|
+
- `push` — code pushed to repository
|
|
3148
|
+
- `pull_request` — PR opened, closed, merged, edited, synchronized
|
|
3149
|
+
- `pull_request_review` — review submitted
|
|
3150
|
+
- `issues` — issue opened, closed, edited, labeled
|
|
3151
|
+
- `issue_comment` — comment on issue or PR
|
|
3152
|
+
- `workflow_run` — GitHub Actions workflow started/completed
|
|
3153
|
+
- `workflow_job` — individual job within a workflow
|
|
3154
|
+
- `check_suite` — check suite created/completed
|
|
3155
|
+
- `check_run` — check run created/completed
|
|
3156
|
+
- `deployment` — deployment created
|
|
3157
|
+
- `deployment_status` — deployment status changed
|
|
3158
|
+
- `label` — label created/edited/deleted
|
|
3159
|
+
|
|
3160
|
+
#### Bidirectional Sync Flow
|
|
3161
|
+
|
|
3162
|
+
**Inbound (GitHub → Kradle):**
|
|
3163
|
+
1. GitHub fires webhook to Kradle endpoint
|
|
3164
|
+
2. `webhookController.processDelivery()` — verify HMAC, dedup, queue
|
|
3165
|
+
3. `syncController.normalizeEvent()` — raw → canonical format
|
|
3166
|
+
4. `syncController.upsertResource()` — create/update local resource with external envelope
|
|
3167
|
+
5. `syncController.updateWatermark()` — advance high-watermark
|
|
3168
|
+
6. Event bus emits resource-change → SSE → UI updates
|
|
3169
|
+
|
|
3170
|
+
**Outbound (Kradle → GitHub):**
|
|
3171
|
+
1. User creates/modifies resource in Kradle
|
|
3172
|
+
2. `writeController.createWriteIntent()` — queue write with idempotency key
|
|
3173
|
+
3. If approval required: pause at PendingApproval
|
|
3174
|
+
4. `writeController.executeWriteIntent()` — call GitHub API via adapter
|
|
3175
|
+
5. On success: mark Succeeded, update ExternalObjectLink
|
|
3176
|
+
6. On failure: retry up to maxRetries, then mark Failed
|
|
3177
|
+
|
|
3178
|
+
#### Conflict Handling
|
|
3179
|
+
|
|
3180
|
+
When GitHub and Kradle disagree on a field value:
|
|
3181
|
+
1. `conflictController.detectConflict()` — compares localValue vs externalValue
|
|
3182
|
+
2. If different: creates ExternalSyncConflict (phase: Open)
|
|
3183
|
+
3. Resolution options:
|
|
3184
|
+
- Auto-resolve via ExternalBackendSyncPolicy conflictResolution setting
|
|
3185
|
+
- Manual resolve via UI/API
|
|
3186
|
+
4. `conflictController.resolveConflict()` — applies chosen strategy
|
|
3187
|
+
5. `conflictController.supersededCheck()` — cleans up when new sync arrives
|
|
3188
|
+
|
|
3189
|
+
---
|
|
3190
|
+
|
|
3191
|
+
### 2.3 Issue/Project Relationships
|
|
3192
|
+
|
|
3193
|
+
#### How KradleProject Groups Issues
|
|
3194
|
+
|
|
3195
|
+
1. **Project Definition:**
|
|
3196
|
+
- `spec.workflowColumns[]` defines kanban columns (e.g., Backlog, In Progress, Done)
|
|
3197
|
+
- Each column has: `{ id, displayName, color, default? }`
|
|
3198
|
+
- `spec.repositoryRefs[]` links project to repositories
|
|
3199
|
+
|
|
3200
|
+
2. **Issue → Project Association:**
|
|
3201
|
+
- Issues reference projects via `spec.projectRefs[]`
|
|
3202
|
+
- Multiple issues can belong to one project
|
|
3203
|
+
- One issue can belong to multiple projects
|
|
3204
|
+
|
|
3205
|
+
3. **Kanban Board Derivation:**
|
|
3206
|
+
- Board columns come from `project.spec.workflowColumns`
|
|
3207
|
+
- Issues are placed in columns based on `issue.spec.workflowState`
|
|
3208
|
+
- Default column: first column with `default: true`, or first column overall
|
|
3209
|
+
|
|
3210
|
+
4. **Drag-Drop Updates:**
|
|
3211
|
+
- Moving issue between columns updates `issue.spec.workflowState` to target column ID
|
|
3212
|
+
- Triggers event bus → SSE → UI update
|
|
3213
|
+
|
|
3214
|
+
5. **External Issues (GitHub → Kradle):**
|
|
3215
|
+
- GitHub issues synced via ExternalBackendBinding
|
|
3216
|
+
- External issue gets ExternalObjectLink with nativeId
|
|
3217
|
+
- workflowState mapped from GitHub project column (if synced)
|
|
3218
|
+
- Labels and assignees synchronized bidirectionally
|
|
3219
|
+
|
|
3220
|
+
6. **Work Item Links:**
|
|
3221
|
+
- `WorkItemSessionLink`: connects issues to agent sessions that worked on them
|
|
3222
|
+
- `WorkItemWorkspaceLink`: connects issues to workspaces containing related work
|
|
3223
|
+
- Enables: "which agent sessions touched this issue?" queries
|
|
3224
|
+
|
|
3225
|
+
---
|
|
3226
|
+
|
|
3227
|
+
### 2.4 GitHub Project Sync Plan
|
|
3228
|
+
|
|
3229
|
+
`githubProjectIssueSyncPlan()` produces a plan with these actions:
|
|
3230
|
+
1. `syncProjectItem` — sync issue to/from GitHub Project board
|
|
3231
|
+
2. `syncIssueMetadata` — sync labels, assignees, state
|
|
3232
|
+
3. `syncRepositoryLinks` — sync repository associations
|
|
3233
|
+
|
|
3234
|
+
---
|
|
3235
|
+
|
|
3236
|
+
## PART 3: Runs, Runners, and Pipeline Integration
|
|
3237
|
+
|
|
3238
|
+
---
|
|
3239
|
+
|
|
3240
|
+
### 3.1 Run Lifecycle (AgentDispatchRun)
|
|
3241
|
+
|
|
3242
|
+
The complete lifecycle of an agent dispatch run:
|
|
3243
|
+
|
|
3244
|
+
#### Phase 1: Initiation
|
|
3245
|
+
|
|
3246
|
+
**Manual Dispatch (`createManualDispatch()`):**
|
|
3247
|
+
1. **Stack Resolution**: Find AgentStack by name in resources
|
|
3248
|
+
- Error if not found: `stack-not-found`
|
|
3249
|
+
|
|
3250
|
+
2. **Permission Review**: `permissionReviewer.reviewPermissions()`
|
|
3251
|
+
- Inputs: repository, ref, actor, agentStack, resources
|
|
3252
|
+
- Outcomes:
|
|
3253
|
+
- `allowed` → proceed to workspace provisioning
|
|
3254
|
+
- `denied` → return error with review details
|
|
3255
|
+
- `requires-approval` → create approval, return early
|
|
3256
|
+
|
|
3257
|
+
3. **Memory Snapshot**: If AgentMemoryRepository exists:
|
|
3258
|
+
- Resolve time-travel (mode: current)
|
|
3259
|
+
- Create AgentMemorySnapshot with resolved commit
|
|
3260
|
+
- Pin memory state for reproducibility
|
|
3261
|
+
|
|
3262
|
+
4. **Approval Gate** (if requires-approval):
|
|
3263
|
+
- Create AgentApproval (action: 'secret-access')
|
|
3264
|
+
- Create AgentDispatchRun with phase: `AwaitingApproval`
|
|
3265
|
+
- Return early — human must approve before continuing
|
|
3266
|
+
|
|
3267
|
+
**Trigger-Based Dispatch (`processEvent()`):**
|
|
3268
|
+
1. Evaluate event against all AgentTriggerRule resources
|
|
3269
|
+
2. For each matching rule (not deduplicated):
|
|
3270
|
+
- Create AgentTriggerExecution record
|
|
3271
|
+
- Call `createManualDispatch()` with rule's agentStack and taskKind
|
|
3272
|
+
3. Track: processed, dispatched, skipped counts
|
|
3273
|
+
|
|
3274
|
+
#### Phase 2: Workspace Provisioning
|
|
3275
|
+
|
|
3276
|
+
5. **Find Reusable Workspace**: `findReusableWorkspace()`
|
|
3277
|
+
- Match: same org + same repository + same branch + phase=Ready
|
|
3278
|
+
- If found: `claimWorkspace()` → phase: InUse
|
|
3279
|
+
|
|
3280
|
+
6. **Create New Workspace** (if no reusable):
|
|
3281
|
+
- `createWorkspace()` → generates:
|
|
3282
|
+
- KradleWorkspace resource (phase: Pending)
|
|
3283
|
+
- PersistentVolumeClaim manifest
|
|
3284
|
+
- PVC: storageClassName=standard, capacity=10Gi, ReadWriteOnce
|
|
3285
|
+
|
|
3286
|
+
7. **Mount Spec**: `getMountSpec()` → volume + volumeMount for pod spec
|
|
3287
|
+
|
|
3288
|
+
#### Phase 3: Context Assembly
|
|
3289
|
+
|
|
3290
|
+
8. **Context Bundle**: `assembleContextBundle()`
|
|
3291
|
+
- Gathers: stack spec prompts, context labels, repository info, source refs
|
|
3292
|
+
- Applies: redaction scanning
|
|
3293
|
+
- Produces: immutable bundle with content-addressable digest
|
|
3294
|
+
|
|
3295
|
+
#### Phase 4: Resource Creation
|
|
3296
|
+
|
|
3297
|
+
9. **Create AgentDispatchRun**:
|
|
3298
|
+
- Spec: organizationRef, repository, sourceRefs, agentStack, taskKind, contextBundleRef
|
|
3299
|
+
- Optional: memorySnapshotRef, workspaceRef, mountSpec
|
|
3300
|
+
- Status: phase=Pending, queuedAt=now
|
|
3301
|
+
|
|
3302
|
+
10. **Create AgentDispatchAttempt**:
|
|
3303
|
+
- Spec: agentDispatchRun, attemptReason='initial', agentStackSnapshot (frozen)
|
|
3304
|
+
- Status: permissionSnapshot, queueEnteredAt
|
|
3305
|
+
|
|
3306
|
+
#### Phase 5: Session Launch
|
|
3307
|
+
|
|
3308
|
+
11. **Agent Mux Client Check**: `agentMuxClient.isAvailable()`
|
|
3309
|
+
- If unavailable: phase=Queued, condition `AgentMuxBound: False (Unavailable)`
|
|
3310
|
+
|
|
3311
|
+
12. **Launch Session**: `agentMuxClient.launchSession({ stack, contextBundle, permissionSnapshot })`
|
|
3312
|
+
- Success: returns `{ runId, sessionId }`
|
|
3313
|
+
- Sets: attempt.status.agentMuxRunId, agentMuxSessionId
|
|
3314
|
+
- Run phase → Running, attempt.status.startedAt
|
|
3315
|
+
|
|
3316
|
+
13. **SSE Subscription**: `agentMuxClient.subscribeToEvents(runId, handler)`
|
|
3317
|
+
- Streams real-time events from Agent Mux
|
|
3318
|
+
- Events collected in array for transcript reconciliation
|
|
3319
|
+
- Run status: `sseSubscription: { runId, active: true }`
|
|
3320
|
+
|
|
3321
|
+
14. **Transcript Creation**: `agentMuxClient.reconcileTranscript(sessionId, events)`
|
|
3322
|
+
- Creates AgentSessionTranscript resource
|
|
3323
|
+
- Run status: transcriptRef set
|
|
3324
|
+
|
|
3325
|
+
#### Phase 6: Completion
|
|
3326
|
+
|
|
3327
|
+
15. **Success**: Agent completes task
|
|
3328
|
+
- Run phase → Succeeded
|
|
3329
|
+
- Workspace released: `releaseWorkspace()` → phase: Ready
|
|
3330
|
+
- Artifacts emitted as KradleArtifact resources
|
|
3331
|
+
|
|
3332
|
+
16. **Failure**: Agent fails or times out
|
|
3333
|
+
- Run phase → Failed
|
|
3334
|
+
- May create new attempt (retry) with attemptReason='retry'
|
|
3335
|
+
- Workspace may be retained for debugging
|
|
3336
|
+
|
|
3337
|
+
---
|
|
3338
|
+
|
|
3339
|
+
### 3.2 Runner System
|
|
3340
|
+
|
|
3341
|
+
#### RunnerPool Resource Management
|
|
3342
|
+
|
|
3343
|
+
**Pool Validation:**
|
|
3344
|
+
- metadata.name: required
|
|
3345
|
+
- organizationRef: required, non-empty
|
|
3346
|
+
- warmReplicas: non-negative integer
|
|
3347
|
+
- maxReplicas: positive integer, >= warmReplicas
|
|
3348
|
+
|
|
3349
|
+
**Pool Status Tracking:**
|
|
3350
|
+
```
|
|
3351
|
+
{ poolName, idle, active, terminating, total, desired, maxReplicas, phase, scaling }
|
|
3352
|
+
```
|
|
3353
|
+
- Phase: Empty (no runners) | Active (runners executing) | Idle (runners waiting)
|
|
3354
|
+
- Scaling: ScalingUp (total < desired) | ScalingDown (total > max) | Stable
|
|
3355
|
+
|
|
3356
|
+
**Capacity Tracking:**
|
|
3357
|
+
```
|
|
3358
|
+
{ poolName, maxReplicas, used, available, utilizationPct }
|
|
3359
|
+
```
|
|
3360
|
+
- used: runners with status=Running
|
|
3361
|
+
- available: maxReplicas - used
|
|
3362
|
+
- utilizationPct: (used/maxReplicas) * 100
|
|
3363
|
+
|
|
3364
|
+
#### Runner Lifecycle
|
|
3365
|
+
|
|
3366
|
+
**Creation (`createRunner()`):**
|
|
3367
|
+
- Generates unique ID: `runner-{poolName}-{timestamp}-{random}`
|
|
3368
|
+
- Status: `Idle` (pre-warmed) or `Running` (assigned to job)
|
|
3369
|
+
- Produces pod spec for Kubernetes
|
|
3370
|
+
|
|
3371
|
+
**Termination (`terminateRunner()`):**
|
|
3372
|
+
- Status → Terminating
|
|
3373
|
+
- Removes job assignment
|
|
3374
|
+
- Removes from registry
|
|
3375
|
+
|
|
3376
|
+
**Job Scheduling (`scheduleJob()`):**
|
|
3377
|
+
1. Check if job already assigned → return existing runner (reused)
|
|
3378
|
+
2. Find idle runner in pool → assign job, status → Running
|
|
3379
|
+
3. Check capacity → if available, create new runner
|
|
3380
|
+
4. No capacity → error: `no-capacity`
|
|
3381
|
+
|
|
3382
|
+
#### Pod Spec Generation
|
|
3383
|
+
|
|
3384
|
+
`generatePodSpec()` produces:
|
|
3385
|
+
```yaml
|
|
3386
|
+
apiVersion: v1
|
|
3387
|
+
kind: Pod
|
|
3388
|
+
metadata:
|
|
3389
|
+
name: runner-{runnerId}
|
|
3390
|
+
namespace: {org namespace}
|
|
3391
|
+
labels:
|
|
3392
|
+
kradle.a5c.ai/runner: {runnerId}
|
|
3393
|
+
kradle.a5c.ai/pool: {poolName}
|
|
3394
|
+
kradle.a5c.ai/org: {orgRef}
|
|
3395
|
+
spec:
|
|
3396
|
+
serviceAccountName: {configured or 'kradle-runner'}
|
|
3397
|
+
restartPolicy: Never
|
|
3398
|
+
containers:
|
|
3399
|
+
- name: runner
|
|
3400
|
+
image: {pool.spec.image or 'ubuntu:24.04'}
|
|
3401
|
+
env:
|
|
3402
|
+
- name: KRADLE_ORG
|
|
3403
|
+
value: {organizationRef}
|
|
3404
|
+
- name: KRADLE_RUN_ID
|
|
3405
|
+
value: {runId}
|
|
3406
|
+
- name: KRADLE_WORKSPACE_PATH
|
|
3407
|
+
value: /workspace
|
|
3408
|
+
volumeMounts:
|
|
3409
|
+
- name: workspace
|
|
3410
|
+
mountPath: /workspace
|
|
3411
|
+
resources:
|
|
3412
|
+
limits: {cpu: '2', memory: '4Gi'}
|
|
3413
|
+
requests: {cpu: '500m', memory: '1Gi'}
|
|
3414
|
+
volumes:
|
|
3415
|
+
- name: workspace
|
|
3416
|
+
persistentVolumeClaim:
|
|
3417
|
+
claimName: kradle-ws-{runId}
|
|
3418
|
+
```
|
|
3419
|
+
|
|
3420
|
+
---
|
|
3421
|
+
|
|
3422
|
+
### 3.3 Argo CD / KubeVela Relationship
|
|
3423
|
+
|
|
3424
|
+
#### Argo CD for GitOps Deployment
|
|
3425
|
+
|
|
3426
|
+
Kradle uses Argo CD for GitOps-based deployment of itself. The integration is in
|
|
3427
|
+
`argocd-gitops.js`.
|
|
3428
|
+
|
|
3429
|
+
**Application Resource (`createArgoCdApplication()`):**
|
|
3430
|
+
```yaml
|
|
3431
|
+
apiVersion: argoproj.io/v1alpha1
|
|
3432
|
+
kind: Application
|
|
3433
|
+
metadata:
|
|
3434
|
+
name: kradle
|
|
3435
|
+
namespace: argocd
|
|
3436
|
+
labels:
|
|
3437
|
+
app.kubernetes.io/part-of: kradle
|
|
3438
|
+
kradle.a5c.ai/gitops-engine: argocd
|
|
3439
|
+
spec:
|
|
3440
|
+
project: default
|
|
3441
|
+
source:
|
|
3442
|
+
repoURL: {configured repo URL}
|
|
3443
|
+
targetRevision: HEAD
|
|
3444
|
+
path: charts/kradle
|
|
3445
|
+
destination:
|
|
3446
|
+
server: https://kubernetes.default.svc
|
|
3447
|
+
namespace: kradle-system
|
|
3448
|
+
syncPolicy:
|
|
3449
|
+
automated:
|
|
3450
|
+
prune: true
|
|
3451
|
+
selfHeal: true
|
|
3452
|
+
syncOptions:
|
|
3453
|
+
- CreateNamespace=true
|
|
3454
|
+
```
|
|
3455
|
+
|
|
3456
|
+
**GitOps Plan (`createKradleGitOpsPlan()`):**
|
|
3457
|
+
- Engine: argocd
|
|
3458
|
+
- Required cluster resources: Application.argoproj.io, Namespace, ServiceAccount, RBAC, APIService, Kradle CRDs
|
|
3459
|
+
- Sync guarantees: automated prune, automated selfHeal, namespace creation
|
|
3460
|
+
|
|
3461
|
+
**Label Convention:**
|
|
3462
|
+
- `kradle.a5c.ai/gitops-engine: argocd` — identifies GitOps-managed resources
|
|
3463
|
+
|
|
3464
|
+
#### KubeVela for Delivery Abstractions
|
|
3465
|
+
|
|
3466
|
+
KubeVela provides OAM (Open Application Model) delivery abstractions:
|
|
3467
|
+
|
|
3468
|
+
**Discovered Resources (core.oam.dev group):**
|
|
3469
|
+
- KubeVelaApplication — OAM application definition
|
|
3470
|
+
- KubeVelaApplicationRevision — revision history
|
|
3471
|
+
- KubeVelaComponentDefinition — component type definitions
|
|
3472
|
+
- KubeVelaWorkloadDefinition — workload type definitions
|
|
3473
|
+
- KubeVelaTraitDefinition — trait type definitions
|
|
3474
|
+
- KubeVelaScopeDefinition — scope type definitions
|
|
3475
|
+
- KubeVelaPolicyDefinition — policy type definitions
|
|
3476
|
+
- KubeVelaPolicy — policy instances
|
|
3477
|
+
- KubeVelaWorkflowStepDefinition — workflow step definitions
|
|
3478
|
+
- KubeVelaWorkflow — workflow instances
|
|
3479
|
+
- KubeVelaResourceTracker — resource tracking (cluster-scoped)
|
|
3480
|
+
|
|
3481
|
+
**KubeVela Integration Points:**
|
|
3482
|
+
- Default namespace: `vela-system` (configurable via `KRADLE_KUBEVELA_NAMESPACE`)
|
|
3483
|
+
- Discovered via CRD listing during snapshot
|
|
3484
|
+
- ResourceTracker is cluster-scoped (not namespaced)
|
|
3485
|
+
- Applications are org-scoped (live in org namespaces)
|
|
3486
|
+
|
|
3487
|
+
**When KubeVela is NOT installed:**
|
|
3488
|
+
- Deployments page shows fallback pipeline visualization
|
|
3489
|
+
- No OAM resources in snapshot
|
|
3490
|
+
- CRD discovery reports empty for core.oam.dev group
|
|
3491
|
+
|
|
3492
|
+
**When Kyverno is NOT installed:**
|
|
3493
|
+
- Policies page shows informational banner
|
|
3494
|
+
- No Kyverno resources in snapshot
|
|
3495
|
+
- PolicyBinding enforce mode may be blocked (if `KRADLE_KYVERNO_REQUIRE_FOR_ENFORCE_MODE=true`)
|
|
3496
|
+
|
|
3497
|
+
---
|
|
3498
|
+
|
|
3499
|
+
### 3.4 External Pipeline Integration
|
|
3500
|
+
|
|
3501
|
+
#### Pipeline/Job ↔ External CI Systems
|
|
3502
|
+
|
|
3503
|
+
**GitHub Actions Integration via ExternalBackendBinding:**
|
|
3504
|
+
|
|
3505
|
+
1. **Webhook Events:**
|
|
3506
|
+
- `workflow_run` → Pipeline resource projection
|
|
3507
|
+
- `workflow_job` → Job resource projection
|
|
3508
|
+
- `check_suite` → Pipeline status update
|
|
3509
|
+
- `check_run` → Job status update
|
|
3510
|
+
|
|
3511
|
+
2. **Event Flow:**
|
|
3512
|
+
```
|
|
3513
|
+
GitHub webhook → ExternalWebhookDelivery → normalizeEvent() → Pipeline/Job upsert
|
|
3514
|
+
```
|
|
3515
|
+
|
|
3516
|
+
3. **Pipeline Phase Mapping:**
|
|
3517
|
+
- GitHub `queued` → Pipeline phase `Queued`
|
|
3518
|
+
- GitHub `in_progress` → Pipeline phase `Running`
|
|
3519
|
+
- GitHub `completed` + conclusion `success` → Pipeline phase `Succeeded`
|
|
3520
|
+
- GitHub `completed` + conclusion `failure` → Pipeline phase `Failed`
|
|
3521
|
+
|
|
3522
|
+
4. **Job Phase Mapping:**
|
|
3523
|
+
- Similar to Pipeline but at individual job level
|
|
3524
|
+
- Tracks: startedAt, completedAt, conclusion
|
|
3525
|
+
|
|
3526
|
+
**Check Run Integration:**
|
|
3527
|
+
- `GitHubCicd.createCheck()` — create check run on commit
|
|
3528
|
+
- `GitHubCicd.updateCheck()` — update check status/conclusion
|
|
3529
|
+
- Used by: Kradle to report agent dispatch status back to GitHub
|
|
3530
|
+
|
|
3531
|
+
**Runner Integration (External Runners):**
|
|
3532
|
+
- GitHub Actions self-hosted runners (ARC) can connect to RunnerPool
|
|
3533
|
+
- External runners register via RunnerPool configuration
|
|
3534
|
+
- Trust tier: `trusted` (internal runners) vs `untrusted` (fork runners)
|
|
3535
|
+
- Capacity tracked: external runners count toward pool maxReplicas
|
|
3536
|
+
|
|
3537
|
+
#### Pipeline Visualization (RunnerScheduler)
|
|
3538
|
+
|
|
3539
|
+
`RunnerScheduler.startPipeline()`:
|
|
3540
|
+
- Creates Pipeline resource with ordered steps
|
|
3541
|
+
- Creates Job per step with service account isolation
|
|
3542
|
+
- Jobs start sequentially (first Running, rest Pending)
|
|
3543
|
+
- Trust tier propagated to job labels
|
|
3544
|
+
|
|
3545
|
+
`RunnerScheduler.rerunFromStep()`:
|
|
3546
|
+
- Creates new pipeline named `{original}-rerun-{step}`
|
|
3547
|
+
- Preserves same steps but resumes from specified step
|
|
3548
|
+
- Maintains trust tier from original pipeline
|
|
3549
|
+
|
|
3550
|
+
---
|
|
3551
|
+
|
|
3552
|
+
## PART 4: Cross-cutting Relationships
|
|
3553
|
+
|
|
3554
|
+
---
|
|
3555
|
+
|
|
3556
|
+
### 4.1 Resource Dependency Graph
|
|
3557
|
+
|
|
3558
|
+
#### AgentStack References (hub resource)
|
|
3559
|
+
```
|
|
3560
|
+
AgentStack
|
|
3561
|
+
├── AgentSubagent[] (via subagentRefs)
|
|
3562
|
+
├── AgentToolProfile (via toolPolicy/toolPolicyRef)
|
|
3563
|
+
├── AgentMcpServer[] (via mcpServerRefs)
|
|
3564
|
+
├── AgentSkill[] (via skillRefs)
|
|
3565
|
+
├── AgentContextLabel[] (via contextLabelRefs)
|
|
3566
|
+
├── AgentServiceAccount (via runtimeIdentity)
|
|
3567
|
+
├── KradleWorkspacePolicy (via workspacePolicy)
|
|
3568
|
+
├── AgentProviderConfig (via provider)
|
|
3569
|
+
└── AgentAdapter (via adapter)
|
|
3570
|
+
```
|
|
3571
|
+
|
|
3572
|
+
#### AgentDispatchRun References
|
|
3573
|
+
```
|
|
3574
|
+
AgentDispatchRun
|
|
3575
|
+
├── AgentStack (via agentStack)
|
|
3576
|
+
├── Repository (via repository)
|
|
3577
|
+
├── KradleWorkspace (via workspaceRef)
|
|
3578
|
+
├─��� AgentContextBundle (via contextBundleRef)
|
|
3579
|
+
├── AgentMemorySnapshot (via memorySnapshotRef)
|
|
3580
|
+
├── AgentDispatchAttempt[] (child resources)
|
|
3581
|
+
├── AgentSession (child, via Attempt)
|
|
3582
|
+
├── AgentApproval[] (gating resources)
|
|
3583
|
+
├── KradleArtifact[] (outputs)
|
|
3584
|
+
└── AgentSessionTranscript (via transcriptRef)
|
|
3585
|
+
```
|
|
3586
|
+
|
|
3587
|
+
#### AgentSession References
|
|
3588
|
+
```
|
|
3589
|
+
AgentSession
|
|
3590
|
+
├── AgentDispatchRun (via dispatchRun)
|
|
3591
|
+
├── AgentSessionTranscript (1:1)
|
|
3592
|
+
├── AgentSessionAttachment[] (0:N)
|
|
3593
|
+
├── WorkItemSessionLink[] (to Issues/PRs)
|
|
3594
|
+
└── KradleWorkspace (bound workspace)
|
|
3595
|
+
```
|
|
3596
|
+
|
|
3597
|
+
#### ExternalBackendBinding References
|
|
3598
|
+
```
|
|
3599
|
+
ExternalBackendBinding
|
|
3600
|
+
├── ExternalBackendProvider (via providerRef)
|
|
3601
|
+
├── ExternalBackendSyncPolicy (controls sync)
|
|
3602
|
+
├── ExternalWebhookDelivery[] (inbound events)
|
|
3603
|
+
├── ExternalSyncEvent[] (normalized events)
|
|
3604
|
+
├── ExternalSyncState[] (watermarks)
|
|
3605
|
+
├── ExternalWriteIntent[] (outbound writes)
|
|
3606
|
+
├── ExternalSyncConflict[] (detected conflicts)
|
|
3607
|
+
├── ExternalObjectLink[] (identity mappings)
|
|
3608
|
+
└── Repository (sync target)
|
|
3609
|
+
```
|
|
3610
|
+
|
|
3611
|
+
#### KradleProject References
|
|
3612
|
+
```
|
|
3613
|
+
KradleProject
|
|
3614
|
+
├── Issue[] (via issue.projectRefs)
|
|
3615
|
+
├── Repository[] (via repositoryRefs)
|
|
3616
|
+
├── AgentStack[] (via stackRefs)
|
|
3617
|
+
└── ExternalBackendBinding (for GitHub Projects sync)
|
|
3618
|
+
```
|
|
3619
|
+
|
|
3620
|
+
#### Pipeline References
|
|
3621
|
+
```
|
|
3622
|
+
Pipeline
|
|
3623
|
+
├── Repository (via repository)
|
|
3624
|
+
├── Job[] (child resources)
|
|
3625
|
+
├���─ RunnerPool (scheduling target)
|
|
3626
|
+
└── ExternalObjectLink (GitHub Actions workflow_run)
|
|
3627
|
+
```
|
|
3628
|
+
|
|
3629
|
+
---
|
|
3630
|
+
|
|
3631
|
+
### 4.2 Namespace Topology
|
|
3632
|
+
|
|
3633
|
+
#### Platform Namespace (`kradle-system`)
|
|
3634
|
+
Contains platform-scoped resources that span all organizations:
|
|
3635
|
+
- Organization (all org definitions)
|
|
3636
|
+
- OrgNamespaceBinding (all namespace bindings)
|
|
3637
|
+
|
|
3638
|
+
#### Organization Namespace (`kradle-org-{slug}`)
|
|
3639
|
+
Contains all org-scoped resources:
|
|
3640
|
+
- Identity: User, Team, Invite, IdentityMapping, AuthProvider, AgentServiceAccount
|
|
3641
|
+
- Repository: Repository, SSHKey, RepositoryPermission, BranchProtection, RefPolicy, WebhookSubscription
|
|
3642
|
+
- Agents: AgentStack, AgentSubagent, AgentToolProfile, AgentMcpServer, AgentSkill, AgentTriggerRule, AgentContextLabel, KradleWorkspacePolicy, AgentAdapter, AgentTransportBinding, AgentProviderConfig, KradleProject, AgentGatewayConfig
|
|
3643
|
+
- Memory: AgentMemoryRepository, AgentMemorySource, AgentMemoryOntology, AgentMemoryAssociation
|
|
3644
|
+
- Workspace: KradleWorkspace
|
|
3645
|
+
- External: ExternalBackendProvider, ExternalBackendBinding, ExternalBackendSyncPolicy, ExternalProviderCapabilityManifest
|
|
3646
|
+
- Policy: PolicyProfile, PolicyTemplate, PolicyBinding, PolicyExceptionRequest
|
|
3647
|
+
- Runners: RunnerPool
|
|
3648
|
+
- UI: View, Selector
|
|
3649
|
+
- Aggregated (postgres): PullRequest, Issue, Review, Pipeline, Job, WebhookDelivery, all Agent* aggregated kinds, all External* aggregated kinds
|
|
3650
|
+
|
|
3651
|
+
#### Cross-Namespace References
|
|
3652
|
+
- Resources do NOT reference across org namespaces (org isolation guarantee)
|
|
3653
|
+
- Platform-scoped resources (Organization, OrgNamespaceBinding) are in `kradle-system`
|
|
3654
|
+
- Org-scoped resources resolve their namespace via `resolveResourceOrg()`
|
|
3655
|
+
- If `metadata.namespace` conflicts with org namespace → error thrown
|
|
3656
|
+
- If org label conflicts with spec.organizationRef → error thrown
|
|
3657
|
+
|
|
3658
|
+
#### Org Isolation Guarantees
|
|
3659
|
+
- Each org gets exactly one namespace
|
|
3660
|
+
- Resources cannot reference resources in other org namespaces
|
|
3661
|
+
- Snapshot enumeration only queries known org namespaces
|
|
3662
|
+
- `withOrgScope()` enforces namespace consistency at apply time
|
|
3663
|
+
- RBAC via AgentRoleBinding is scoped to org namespace
|
|
3664
|
+
|
|
3665
|
+
---
|
|
3666
|
+
|
|
3667
|
+
### 4.3 Event Propagation
|
|
3668
|
+
|
|
3669
|
+
#### Resource Change → UI Update
|
|
3670
|
+
```
|
|
3671
|
+
Resource apply/delete
|
|
3672
|
+
→ event-bus.emitResourceChange(kind, name, operation)
|
|
3673
|
+
→ globalEventBus.emit({ type: 'resource-change', kind, name, operation, timestamp })
|
|
3674
|
+
→ SSE endpoint streams to connected clients
|
|
3675
|
+
→ React UI receives event → refetches affected resources
|
|
3676
|
+
```
|
|
3677
|
+
|
|
3678
|
+
#### Webhook → Resource Update → UI
|
|
3679
|
+
```
|
|
3680
|
+
External webhook (GitHub, etc.)
|
|
3681
|
+
→ webhookController.processDelivery() [verify HMAC, dedup]
|
|
3682
|
+
→ syncController.normalizeEvent() [raw → canonical]
|
|
3683
|
+
→ syncController.upsertResource() [create/update with envelope]
|
|
3684
|
+
→ syncController.updateWatermark() [advance cursor]
|
|
3685
|
+
→ event-bus.emitResourceChange()
|
|
3686
|
+
→ SSE → UI update
|
|
3687
|
+
```
|
|
3688
|
+
|
|
3689
|
+
#### Trigger Rule → Dispatch → Session → Events
|
|
3690
|
+
```
|
|
3691
|
+
Event arrives (push, PR, issue, cron, webhook, comment, label)
|
|
3692
|
+
→ triggerController.evaluateEvent() [match rules, dedup]
|
|
3693
|
+
→ triggerController.createTriggerExecution() [audit record]
|
|
3694
|
+
→ dispatchController.createManualDispatch() [full orchestration]
|
|
3695
|
+
→ agentMuxClient.launchSession() [start agent]
|
|
3696
|
+
→ agentMuxClient.subscribeToEvents() [SSE from agent]
|
|
3697
|
+
→ agentMuxClient.reconcileTranscript() [build transcript]
|
|
3698
|
+
→ event-bus.emitResourceChange('AgentDispatchRun', ...)
|
|
3699
|
+
→ SSE → UI shows running session
|
|
3700
|
+
```
|
|
3701
|
+
|
|
3702
|
+
#### Approval Request → User Action → Run Continues
|
|
3703
|
+
```
|
|
3704
|
+
Permission review says 'requires-approval'
|
|
3705
|
+
→ approvalController.createApprovalRequest() [phase: Pending]
|
|
3706
|
+
→ event-bus.emitResourceChange('AgentApproval', ...)
|
|
3707
|
+
→ SSE → UI shows approval request notification
|
|
3708
|
+
→ User clicks approve/deny in UI
|
|
3709
|
+
→ approvalController.recordDecision() [phase: Approved/Denied]
|
|
3710
|
+
→ event-bus.emitResourceChange('AgentApproval', ...)
|
|
3711
|
+
→ If approved: dispatch continues from step 5 (workspace provisioning)
|
|
3712
|
+
→ If denied: run marked Failed
|
|
3713
|
+
```
|
|
3714
|
+
|
|
3715
|
+
#### External Write → Conflict → Resolution
|
|
3716
|
+
```
|
|
3717
|
+
User modifies resource in Kradle
|
|
3718
|
+
→ writeController.createWriteIntent() [queue outbound write]
|
|
3719
|
+
→ If requiresApproval: pause at PendingApproval
|
|
3720
|
+
→ writeController.executeWriteIntent() [call external API]
|
|
3721
|
+
→ On conflict: conflictController.detectConflict()
|
|
3722
|
+
→ ExternalSyncConflict created (phase: Open)
|
|
3723
|
+
→ event-bus → SSE → UI shows conflict
|
|
3724
|
+
→ User resolves via UI
|
|
3725
|
+
→ conflictController.resolveConflict() [strategy applied]
|
|
3726
|
+
→ If prefer-kradle: retry write
|
|
3727
|
+
→ If prefer-external: accept external value, update local
|
|
3728
|
+
```
|
|
3729
|
+
|
|
3730
|
+
---
|
|
3731
|
+
|
|
3732
|
+
### 4.4 Storage Boundaries
|
|
3733
|
+
|
|
3734
|
+
| Storage | Resources | Access Pattern |
|
|
3735
|
+
|---------|-----------|----------------|
|
|
3736
|
+
| etcd (CRDs) | Organization, User, Team, Repository, AgentStack, ExternalBackendProvider, etc. (44 kinds) | kubectl get/apply/delete, K8s watch |
|
|
3737
|
+
| postgres (Aggregated) | PullRequest, Issue, Pipeline, AgentDispatchRun, AgentSession, ExternalWebhookDelivery, etc. (30 kinds) | Aggregated API, snapshot cache |
|
|
3738
|
+
| kubevela | KubeVelaApplication, etc. (12 kinds) | kubectl via core.oam.dev group |
|
|
3739
|
+
| kyverno | KyvernoPolicy, PolicyReport, etc. (10 kinds) | kubectl via kyverno.io group |
|
|
3740
|
+
| core | Secret, ConfigMap | kubectl (not in snapshot, on-demand access) |
|
|
3741
|
+
| repositories | Git repository data | Gitea API, raw git |
|
|
3742
|
+
| objects | Artifacts, attachments | Object storage (referenced by digest) |
|
|
3743
|
+
|
|
3744
|
+
---
|
|
3745
|
+
|
|
3746
|
+
### 4.5 Snapshot Architecture
|
|
3747
|
+
|
|
3748
|
+
The `getControllerSnapshot()` function produces a comprehensive cluster state:
|
|
3749
|
+
|
|
3750
|
+
```javascript
|
|
3751
|
+
{
|
|
3752
|
+
source: 'kubernetes',
|
|
3753
|
+
mode: 'kubernetes-api',
|
|
3754
|
+
namespace, // platform namespace
|
|
3755
|
+
generatedAt, // ISO timestamp
|
|
3756
|
+
correlationId, // UUID for request tracing
|
|
3757
|
+
kubectl: { // kubectl binary status
|
|
3758
|
+
binary, context, clientVersion, available, errors
|
|
3759
|
+
},
|
|
3760
|
+
apiService, // Kradle APIService resource (if exists)
|
|
3761
|
+
crds, // Discovered CRD resources
|
|
3762
|
+
resources: { // All resources by kind
|
|
3763
|
+
Organization: [...],
|
|
3764
|
+
Repository: [...],
|
|
3765
|
+
AgentStack: [...],
|
|
3766
|
+
// ... all 76+ kinds
|
|
3767
|
+
},
|
|
3768
|
+
kyverno: { // Kyverno discovery
|
|
3769
|
+
mode, namespace, detected, controllers, resources, reports, permissions, degraded
|
|
3770
|
+
},
|
|
3771
|
+
events, // K8s events in platform namespace
|
|
3772
|
+
permissions, // RBAC can-i results per resource kind
|
|
3773
|
+
storage, // Storage boundary descriptions
|
|
3774
|
+
commands // kubectl command templates per kind
|
|
3775
|
+
}
|
|
3776
|
+
```
|
|
3777
|
+
|
|
3778
|
+
**Snapshot Enumeration:**
|
|
3779
|
+
1. Platform-scoped resources: listed in `kradle-system`
|
|
3780
|
+
2. Org-scoped resources: listed in each discovered org namespace
|
|
3781
|
+
3. Kyverno resources: listed if CRDs detected
|
|
3782
|
+
4. Stale-while-revalidate: 30s TTL cache
|
|
3783
|
+
|
|
3784
|
+
**In-Cluster Detection:**
|
|
3785
|
+
- Checks `KUBERNETES_SERVICE_HOST` + service account token
|
|
3786
|
+
- Auto-configures kubectl with in-cluster credentials
|
|
3787
|
+
|
|
3788
|
+
---
|
|
3789
|
+
|
|
3790
|
+
## Part 10: Inference Domain
|
|
3791
|
+
|
|
3792
|
+
### 10.1 KradleInferenceService Behavior
|
|
3793
|
+
|
|
3794
|
+
`KradleInferenceService` is a Kradle-owned wrapper around the KServe `InferenceService` CRD in the `serving.kserve.io/v1beta1` API group. The controller (`kradle-inference-service-controller.js`) manages the full lifecycle:
|
|
3795
|
+
|
|
3796
|
+
**On create/update:**
|
|
3797
|
+
1. Validates `spec.predictor.model.modelFormat.name` against `SUPPORTED_MODEL_FORMATS`
|
|
3798
|
+
2. Generates a complete KServe `InferenceService` manifest including predictor, resources, and protocol version
|
|
3799
|
+
3. Applies the manifest to Kubernetes via `kubectl apply`
|
|
3800
|
+
4. Sets `status.phase = 'Pending'`
|
|
3801
|
+
|
|
3802
|
+
**Phase transitions:**
|
|
3803
|
+
- `Pending` → `Ready`: When KServe readiness probe passes and the service URL is available
|
|
3804
|
+
- `Pending` / `Ready` → `Failed`: On error during manifest apply or when KServe reports failure
|
|
3805
|
+
- Status transitions are driven by polling `status.url` from the underlying `InferenceService` resource
|
|
3806
|
+
|
|
3807
|
+
**Endpoint discovery:**
|
|
3808
|
+
- `status.url` is resolved from the underlying KServe `InferenceService.status.url` field
|
|
3809
|
+
- Available only after the service reaches `Ready` phase
|
|
3810
|
+
- Used by `toProviderConfig()` to bridge to `AgentProviderConfig`
|
|
3811
|
+
|
|
3812
|
+
**`toProviderConfig()` bridge:**
|
|
3813
|
+
- Returns an `AgentProviderConfig` with `spec.type: 'kserve'`
|
|
3814
|
+
- Includes the resolved endpoint URL and inference protocol version
|
|
3815
|
+
- Allows `AgentStack` CRDs to route requests to on-cluster inference services alongside cloud LLMs
|
|
3816
|
+
|
|
3817
|
+
**Deletion:**
|
|
3818
|
+
- Deleting the `KradleInferenceService` resource cascades to deletion of the underlying KServe `InferenceService`
|
|
3819
|
+
|
|
3820
|
+
### 10.2 KradleServingRuntime Behavior
|
|
3821
|
+
|
|
3822
|
+
`KradleServingRuntime` wraps the KServe `ServingRuntime` (or `ClusterServingRuntime`) CRD:
|
|
3823
|
+
|
|
3824
|
+
**On create/update:**
|
|
3825
|
+
1. Validates `supportedModelFormats` entries
|
|
3826
|
+
2. Generates and applies the KServe `ServingRuntime` manifest
|
|
3827
|
+
3. Runtime is registered in the KServe runtime registry
|
|
3828
|
+
|
|
3829
|
+
**Lifecycle:**
|
|
3830
|
+
- Independent of individual inference services — a runtime can be referenced by multiple `KradleInferenceService` instances
|
|
3831
|
+
- Deleted only when explicitly removed; does not cascade from inference service deletion
|
|
3832
|
+
|
|
3833
|
+
**Reference from KradleInferenceService:**
|
|
3834
|
+
- `spec.predictor.model.runtime` field names the runtime
|
|
3835
|
+
- KServe uses the runtime to determine the serving container image and configuration
|
|
3836
|
+
|
|
3837
|
+
---
|
|
3838
|
+
|
|
3839
|
+
## Part 11: Artifact Domain
|
|
3840
|
+
|
|
3841
|
+
### 11.1 ArtifactRegistry Lifecycle
|
|
3842
|
+
|
|
3843
|
+
`ArtifactRegistry` is the top-level scope for artifact storage:
|
|
3844
|
+
|
|
3845
|
+
**On create:**
|
|
3846
|
+
1. Allocates storage in the configured backend:
|
|
3847
|
+
- `internal`: uses etcd (small artifacts, dev/test)
|
|
3848
|
+
- `s3` / `gcs` / `azure-blob`: creates/configures cloud bucket paths from `storageConfig`
|
|
3849
|
+
2. If `externalIntegration` is set, establishes connection to external provider
|
|
3850
|
+
3. Sets `status.phase = 'Ready'` when storage is accessible
|
|
3851
|
+
|
|
3852
|
+
**External integration modes:**
|
|
3853
|
+
- `read-only`: proxies reads to external provider; writes are rejected
|
|
3854
|
+
- `read-write`: both reads and writes flow to the external provider
|
|
3855
|
+
- `mirror`: internal storage is primary; published versions are also synced to the external provider
|
|
3856
|
+
|
|
3857
|
+
**Deletion:**
|
|
3858
|
+
- Cascades to all child `ArtifactFeed` resources
|
|
3859
|
+
- Cloud storage data is NOT automatically deleted (requires manual cleanup)
|
|
3860
|
+
|
|
3861
|
+
### 11.2 Feed Management
|
|
3862
|
+
|
|
3863
|
+
`ArtifactFeed` belongs to exactly one `ArtifactRegistry`:
|
|
3864
|
+
|
|
3865
|
+
**Visibility enforcement:**
|
|
3866
|
+
- `public` feeds: all authenticated users can read (download)
|
|
3867
|
+
- `private` feeds: read access requires an `ArtifactAccessPolicy` with `permission: 'read'`
|
|
3868
|
+
|
|
3869
|
+
**Retention policy:**
|
|
3870
|
+
- Enforced on each version publish: after storing the new version, the controller checks `maxVersions` and `maxAgeDays`
|
|
3871
|
+
- Oldest versions pruned first; versions are soft-deleted (phase set to `Archived`) before hard-delete
|
|
3872
|
+
|
|
3873
|
+
**Access policy resolution:**
|
|
3874
|
+
- Fine-grained permissions via `ArtifactAccessPolicy` resources
|
|
3875
|
+
- `write` permission required to publish versions
|
|
3876
|
+
- `admin` permission required to modify feed settings or revoke other policies
|
|
3877
|
+
|
|
3878
|
+
### 11.3 Version Publishing
|
|
3879
|
+
|
|
3880
|
+
`ArtifactVersion` is created via `POST /api/orgs/{org}/artifacts/feeds/{feed}/publish`:
|
|
3881
|
+
|
|
3882
|
+
1. `withAuth` middleware populates `spec.publishedBy` from the session user
|
|
3883
|
+
2. `spec.publishedAt` is set to current ISO 8601 timestamp
|
|
3884
|
+
3. Checksums (`sha256`, `md5`) are computed from the uploaded content and stored
|
|
3885
|
+
4. Version name is derived from `name@version` string; immutable once set
|
|
3886
|
+
5. Retention policy check runs post-publish
|
|
3887
|
+
|
|
3888
|
+
### 11.4 Download Tracking
|
|
3889
|
+
|
|
3890
|
+
`ArtifactDownload` records are written on each package download:
|
|
3891
|
+
|
|
3892
|
+
- Created by the download handler in the artifact feed controller
|
|
3893
|
+
- Captures: `downloadedBy`, `downloadedAt`, `ipAddress`, `userAgent`, `clientId`
|
|
3894
|
+
- Used for analytics dashboards and rate-limiting enforcement
|
|
3895
|
+
- Records are append-only; not modified after creation
|
|
3896
|
+
|
|
3897
|
+
---
|
|
3898
|
+
|
|
3899
|
+
## Part 12: Cross-Domain Relationships
|
|
3900
|
+
|
|
3901
|
+
### 12.1 Inference → Agent
|
|
3902
|
+
|
|
3903
|
+
The inference domain integrates with the agent domain via the provider config bridge:
|
|
3904
|
+
|
|
3905
|
+
1. `KradleInferenceService.toProviderConfig()` creates an `AgentProviderConfig` with:
|
|
3906
|
+
- `spec.type: 'kserve'`
|
|
3907
|
+
- `spec.endpoint`: resolved `status.url` from the inference service
|
|
3908
|
+
- `spec.protocolVersion`: V1 or V2
|
|
3909
|
+
|
|
3910
|
+
2. `AgentStack` references the provider config in `spec.providers[]`
|
|
3911
|
+
|
|
3912
|
+
3. When an agent is dispatched, `resolveStack()` resolves all provider configs and injects the KServe endpoint URL as an env var into the K8s Job
|
|
3913
|
+
|
|
3914
|
+
4. This allows agent stacks to mix cloud LLMs (Anthropic, OpenAI) with on-cluster models (KServe-hosted sklearn, PyTorch, etc.) in the same stack definition
|
|
3915
|
+
|
|
3916
|
+
### 12.2 Artifacts → Repositories
|
|
3917
|
+
|
|
3918
|
+
Artifact feeds can integrate with Kradle repositories:
|
|
3919
|
+
|
|
3920
|
+
- **Version tagging**: ArtifactVersion publish can create a git tag in the associated repository
|
|
3921
|
+
- **Release integration**: In `mirror` mode, published versions are synced to repository releases (GitHub Releases / Gitea Releases)
|
|
3922
|
+
- **Internal feeds**: use Kradle storage (etcd or cloud blob); no repository dependency required
|
|
3923
|
+
- **External feeds**: proxy to the configured provider (e.g., npm registry, PyPI, GitHub Packages); repository serves as the source of truth for release metadata
|
|
3924
|
+
|
|
3925
|
+
The relationship is optional: artifact feeds do not require a repository reference and can operate as standalone package registries.
|
|
3926
|
+
- Falls back to kubeconfig if not in-cluster
|