# KB Repository Import Progress

> Canonical path: `specs/kb/repository-import-progress.md`
> Status: living product-engineering spec
> Scope: user-facing progress for repository-backed Knowledge Base source import, mirror synchronization, and generated repository knowledge.

## 1. Purpose

Repository-backed KB source import can take longer than a single request. Users need to know whether a source is being created, mirrored, analyzed, indexed, ready, or failed after they submit an import.

This spec defines a source-scoped progress contract for repository-backed KB sources. It complements the KB source lifecycle in [`workspace-knowledge-base.md`](./workspace-knowledge-base.md) and the repository-backed storage model in [`repo-backed-knowledge-base.md`](./repo-backed-knowledge-base.md).

The progress surface is a product contract owned by MeshX. It must be derived from durable source, mirror, job, analysis, revision, and error state, not from transient browser state alone.

## 2. Product Goals

The KB UI must:

- give immediate feedback while the source create request is pending;
- keep showing durable progress after the create request returns and background work continues;
- survive page reloads, navigation, and multi-client viewing;
- present import progress on Source rows in Draft setup and isolated Official-edit setup, while normal reader-visible Official Source rows omit the setup progress card;
- make blocking failures actionable with a clear failed stage and retry entry point when retry is supported;
- distinguish optional processing warnings from blocking import failures, so a usable source can be shown as ready with warnings instead of failed;
- let users expand the source detail progress card into a checklist-style stage detail view without requiring that detail view to understand source usability;
- avoid exposing raw clone logs, worker errors, credentials, secret references, or internal stack traces in source list, source detail, or source settings surfaces;
- keep detailed worker failure summaries in the KB overview activity stream where operators expect event diagnostics;
- avoid pretending to have exact byte or file progress unless a worker records measured progress.

## 3. Progress Model

Progress is scoped to one KB source. A source can have several processing phases, but the UI should show one primary active or failed phase at a time.

| Phase | Meaning | Durable owner |
| --- | --- | --- |
| `provisioning` | MeshX is ensuring the source row, AGS owner, user-visible repository, insights repository, repository mappings, and initial repository access intent reconciliation. | The create source request. Durable progress requires a dedicated job if this becomes asynchronous. |
| `mirror_sync` | MeshX is validating the external Git remote and synchronizing it into the source's AGS mirror repository. | `knowledge_base_jobs` with job type `sync_source_mirror`, plus `knowledge_base_source_mirrors`. |
| `analysis` | MeshX is generating repository knowledge, such as insight graph artifacts and wiki pages, from the source repository into the insights repository. | `knowledge_base_jobs` with job type `generate_repo_wiki_from_code`, plus `knowledge_base_analysis_runs` and source revisions. |
| `indexing` | MeshX is updating retrieval indexes or derived searchable state after source content or generated knowledge changes. | Source index state, analysis state, and future indexing jobs. |

### Provisioning boundary

Source provisioning currently completes inside the source create request. The browser may show local button or dialog state such as "creating source" while that request is in flight. That local state is not enough for reload-safe progress.

If provisioning becomes long-running, MeshX must create a durable source import job before starting repository provisioning and expose it through the same progress contract. Until then, persisted import progress starts after the source exists and a background mirror, analysis, or indexing job is enqueued.

## 4. Progress State

The public progress state is intentionally small:

| State | Meaning |
| --- | --- |
| `idle` | No import or processing operation is active, failed, or relevant for the current source view. |
| `queued` | Work is accepted and waiting for a worker. |
| `running` | Work is currently executing. |
| `succeeded` | The latest visible import or processing operation completed and the source is usable. |
| `failed` | The latest visible import or processing operation failed or the source has an import-related persisted error. |

Failure wins over stale success only when the failure blocks source usability or the requested required operation. Optional step failures become warnings and must not force the top-level state to `failed` when the source remains usable. Active work wins over previous terminal state. When multiple jobs are active, the most recently updated source-scoped job is the primary progress item.

The UI may combine `state` with progress severity to derive the user-facing label:

| Derived label | Contract condition | Meaning |
| --- | --- | --- |
| Importing | `state=queued` or `state=running` | Source processing is active. |
| Ready | `state=succeeded` and severity is absent or `success` | The source is usable and no visible warning remains. |
| Ready with warnings | `state=succeeded` and `severity=warning` | The source is usable, but one or more optional steps failed or produced partial results. |
| Failed | `state=failed` or `severity=error` | A required step failed and the source or requested operation needs attention before it is considered ready. |

## 5. Stage Vocabulary

Workers own stable stage tokens. UI copy maps tokens to localized labels and descriptions.

### Mirror sync stages

| Stage | State | Suggested coarse percent | User-facing meaning |
| --- | --- | ---: | --- |
| `queued` | `queued` | 5 | Waiting to start mirror synchronization. |
| `validating_remote` | `running` | 20 | Validating remote URL, credentials, and target ref. |
| `syncing_ags_repo` | `running` | 55 | Synchronizing the external Git remote into the AGS mirror repository. |
| `reconciling_kb_index` | `running` | 80 | Reconciling source metadata, mirrored revision, and follow-up processing. |
| `ready` | `succeeded` | 100 | Mirror synchronization completed. |
| `failed` | `failed` | omitted | Mirror synchronization failed. |

### Repository knowledge generation stages

| Stage | State | Suggested coarse percent | User-facing meaning |
| --- | --- | ---: | --- |
| `queued` | `queued` | 5 | Waiting to generate repository knowledge. |
| `preparing_snapshot` | `running` | 15 | Preparing a repository snapshot for analysis. |
| `generating_insight_graph` | `running` | 45 | Generating the insight graph and repository wiki. |
| `validating_pages` | `running` | 65 | Validating generated pages and metadata. |
| `writing_ags_wiki` | `running` | 80 | Writing generated knowledge to the insights repository wiki. |
| `indexing` | `running` | 90 | Updating searchable or retrieval state. |
| `ready` | `succeeded` | 100 | Generated repository knowledge is ready. |
| `failed` | `failed` | omitted | Repository knowledge generation failed. |

Coarse percentages are display hints only. They must not be used as completion guarantees. A worker may expose measured progress later, but measured progress must be explicitly typed and must not overload these coarse stage percentages.

## 6. API Contract

KB source list and source detail APIs should expose a display-safe import progress summary. Source detail may additionally expose recent jobs and analysis runs for diagnostics, but the default source detail UI should treat those records as status diagnostics rather than as a place to echo worker failure text.

Recommended DTO shape:

```ts
type KBSourceImportProgress = {
  state: "idle" | "queued" | "running" | "succeeded" | "failed"
  severity?: "info" | "success" | "warning" | "error"
  phase?: "provisioning" | "mirror_sync" | "analysis" | "indexing"
  stage?: string
  message?: string
  percent?: number
  currentJobId?: string
  currentJobType?: string
  startedAt?: string
  updatedAt?: string
  finishedAt?: string
  lastError?: string
  steps?: KBSourceImportProgressStep[]
}

type KBSourceImportProgressStep = {
  id: string
  phase?: "provisioning" | "mirror_sync" | "analysis" | "indexing"
  stage?: string
  status: "pending" | "running" | "succeeded" | "warning" | "failed" | "skipped"
  optional?: boolean
  message?: string
  error?: string
  startedAt?: string
  updatedAt?: string
  finishedAt?: string
}
```

Rules:

- `message` and `lastError`, when present, must be display-safe product copy. They must not carry raw worker failure strings, clone logs, stack traces, credentials, credential refs, raw remote URLs with embedded secrets, or local filesystem paths that are not meaningful to the user.
- Failed job errors for optional generated knowledge must not be copied into source list/detail `message`, `lastError`, checklist step detail fields, or default overview activity rows. They belong in an explicit job diagnostic API or another generation-specific surface.
- Step `message` and `error` fields follow the same display boundary as top-level progress fields.
- `percent` is optional. Omit it for failed and unknown states.
- `severity` describes the highest user-visible attention level for the progress summary. If omitted, UI should infer `info` for active states, `success` for `succeeded`, and `error` for `failed`.
- `currentJobId` links the progress summary to source diagnostics; it is not a user-facing identifier.
- `currentJobType` is an API token. UI should localize it before display.
- Step `id`, `phase`, and `stage` are API tokens. UI should map them to localized checklist labels and descriptions instead of requiring server-rendered display copy.
- Required step failure sets the top-level state to `failed` and should also set `severity=error`. Optional step failure sets the step status to `warning`, should set `severity=warning` when no blocking error is present, and should leave the top-level state based on source usability.
- A `skipped` optional step means the step was not configured, not requested, or not applicable to that source. It must not be styled as a warning.
- Existing job-specific endpoints may keep returning job-specific DTOs, but source list/detail should not require the browser to manually interpret raw `progress_json`.

### Overview activity diagnostics

KB overview activity is the default event stream for user-actionable source work such as repository mirror sync. MeshX may project recent source jobs into overview activity with stable actions such as `kb.source.mirror_sync.failed`. Failed activity summaries must be sanitized before they leave the service boundary, and activity rows should identify the source resource so the UI can localize the title and route the user to the affected source. Generated-document worker events such as repository knowledge generation are not shown in the default KB detail activity stream; if users need them, expose them through an explicit job diagnostic surface.

Source list and source detail progress cards should not duplicate those failure summaries. They should show health state, phase, stage, retry actions, timestamps, and checklist status.

### Checklist semantics

The checklist is a detail projection over the same durable source, mirror, job, analysis, revision, and error state as the top-level summary. It is not a separate workflow engine and must not introduce independent truth.

Recommended stable step identities:

| Step id | Phase | Required by default | Meaning |
| --- | --- | --- | --- |
| `source_created` | `provisioning` | yes | MeshX source metadata exists. |
| `repositories_ready` | `provisioning` | yes | Required user-visible and insights repositories are mapped and usable. |
| `mirror_synced` | `mirror_sync` | yes for mirror sources | External Git content is synchronized into the AGS mirror repository. |
| `generated_knowledge` | `analysis` | no | Repository knowledge such as insight graph artifacts and wiki pages has been generated into insights storage. |
| `indexed` | `indexing` | configuration-dependent | Retrieval indexes or derived searchable state are current enough for the configured source behavior. |

Workers may add more specific step ids when they represent durable product milestones, but they must keep the top-level phase/stage summary stable. Provisioning steps may be synthesized as completed from durable source and repository mapping state; they should not display precise timing unless a durable provisioning job or lifecycle event recorded it.

Step status presentation:

- `pending`: not started yet.
- `running`: currently executing.
- `succeeded`: completed successfully.
- `warning`: optional step failed, produced partial results, or completed with a non-blocking condition that affects generated knowledge, retrieval quality, or freshness.
- `failed`: required step failed and blocks readiness for the source or requested operation.
- `skipped`: optional step was not configured, not requested, or not applicable.

The checklist should support check, warning, failure, pending, running, and skipped affordances. It must not require users to inspect the checklist to answer the primary question: whether the source is usable.

### Source detail diagnostics

`KBSourceJob` and `KBSourceAnalysisRun` diagnostics should expose display-safe progress fields when available:

```ts
type KBSourceJob = {
  id: string
  jobType: string
  status: string
  stage?: string
  message?: string
  pageCount?: number
  error?: string
  createdAt: string
  updatedAt: string
  finishedAt?: string
}
```

The API must parse known progress payloads server-side and drop unknown or sensitive fields. The raw `progress_json` string must remain an internal storage detail. Default source detail diagnostics should display status, stage, page count when relevant, and created/updated/finished times; they should not display worker failure `message` or `error` text. Detailed worker failure summaries are shown through KB overview activity or explicit job diagnostic endpoints.

### Mirror sync attempt diagnostics

The existing source-scoped mirror sync endpoint is the explicit diagnostic surface for repository mirror attempts. Each `KBSourceMirrorSyncJob` may expose these display-safe fields in addition to its stable job identity and status:

```ts
type KBSourceMirrorSyncJob = {
  id: string
  status: string
  stage?: string
  failedStage?: string
  error?: string
  errorCode?: string
  retryable?: boolean
  ref?: string
  trigger?: "manual" | "scheduled"
  requestedBy?: string
  traceId?: string
  attemptCount: number
  gitOperation?: "clone" | "ags_push"
  gitAttempt?: number
  gitMaxAttempts?: number
  queueWaitMs?: number
  executionDurationMs?: number
  createdAt: string
  startedAt?: string
  finishedAt?: string
}
```

`error` is allowed only on the explicitly selected sync-attempt detail and must pass the standard KB display sanitizer before persistence and API projection. `errorCode` is a stable, non-secret classification used for localized remediation guidance; expected classes include upstream authentication/not-found, DNS, network, TLS, proxy, missing ref, clone timeout, AGS push, reconciliation, lease loss, and internal failure. List rows remain compact and must not render the error by default. The API must derive these fields server-side from the known mirror progress payload and must never return raw `progress_json`, clone output, credential references, access tokens, or local workspace paths.

## 7. Progress Derivation

MeshX derives `importProgress` from durable state in this order:

1. Find source-scoped queued or running jobs for import-related job types. Use the most recently updated job as the primary progress. However, if the source has already been successfully imported at least once (mirror `lastSuccessAt` is set or `lastMirroredCommit` is non-empty), subsequent background sync jobs must not drive import progress — the initial import is complete and ongoing sync activity is reflected through the mirror's `syncStatus` field, not through import progress. Additionally, `enqueueSourceMirrorSync` must not regress `source.Status` to `queued` for an already-imported source, because that would cause `sourceProvisioningImportProgress` to return a provisioning-queued state (showing "importing 10%") even when the active-job branch is skipped.
2. Build checklist steps from source, repository mapping, mirror, analysis, revision, and job state.
3. If no active job exists, find the latest failed required import-related job, failed source status, failed required mirror status, failed required analysis/index state, or persisted source/mirror error. A failed mirror sync job for a source that has already been successfully imported is a background refresh failure, not an import failure, and must not drive the import progress state to `failed`.
4. If a required failure exists, expose `failed` with `severity=error`, the failed phase/stage, and a matching `failed` checklist step.
5. If the source is usable but one or more optional steps have failed or produced partial results, expose `succeeded` with `severity=warning` and matching `warning` checklist steps.
6. If the latest import-related operation succeeded and the source is usable, expose `succeeded` with the latest relevant terminal phase and timestamp.
7. If none of the above applies, expose `idle`.

Import-related job types include:

- `sync_source_mirror` for mirror synchronization;
- `generate_repo_wiki_from_code` for codebase knowledge generation;
- future source indexing or external-connector materialization jobs when they affect whether the imported source is ready for browsing or retrieval.

A source remains browsable according to its existing source and repository permissions. Progress state describes processing, not authorization.

### Job cancellation on disable and delete

When a mirror source is disabled (`enabled=false`) or deleted, all queued or running `sync_source_mirror` jobs for that source must be cancelled, not failed. A cancelled job has `status=cancelled`, no lease owner, and no error. The mirror and source sync status must reflect the user's intent (`disabled` or `idle`), not a failure. A worker that claims a job for a disabled or deleted source must cancel the job and leave the source/mirror status as `disabled`/`idle` rather than marking them `failed`.

### Worker concurrency and fair scheduling

The mirror sync worker claims up to `SourceMirrorWorkerBatch` jobs per tick (default 3) and runs them concurrently using a goroutine pool. Jobs are claimed in per-source round-robin order: the claimer groups queued jobs by `source_id`, picks the highest-priority candidate for each source, orders those candidates by priority and then FIFO (`created_at`, row id), and picks one job per source per batch. Initial imports whose mirror has never synchronized successfully have higher priority than refreshes of already usable sources, so the next available worker slot serves new user-blocking imports first. Priority does not preempt a running sync. This prevents a single source with many queued jobs from monopolizing the worker while other sources wait.

### Stale running job recovery on startup

When the server starts, it reclaims all `sync_source_mirror` jobs in `running` status whose leases have expired (`lease_until <= now`), resetting them to `queued` with cleared lease fields. This ensures jobs left running by a previous process crash or restart are immediately eligible for the next worker tick instead of waiting for the full lease duration to elapse.

### Atomic job deduplication

`CreateUniqueSourceJob` locks the source row (`SELECT ... FOR UPDATE`) before checking for active jobs, serializing concurrent enqueue attempts for the same source. When an active job already exists, it returns `ErrSourceJobAlreadyActive`. The manual sync path (`RequestSourceMirrorSync`, `failIfExisting=true`) maps this to a 409 Conflict response so the caller knows a sync is already in progress. The scheduler path (`failIfExisting=false`) silently reuses the existing job without mutating mirror or source status.

### Stale success after config change

When `UpdateSourceMirror` changes the remote URL or default ref, the previously synced commit and success timestamp belong to a different repository or branch. They must be cleared (`LastSuccessAt=nil`, `LastMirroredCommit=""`) and `SyncStatus` set to `idle` so the UI does not show stale success state for a remote that has not been synced yet. When the remote and ref are unchanged, the existing success state must be preserved. Additionally, `sourceUsableForImportProgress` must return `false` for a mirror sync job when the mirror has no success state (`LastSuccessAt=nil` and `LastMirroredCommit=""`), so a stale terminal succeeded job from the previous config is not shown as "imported 100%".

### Lease-safe failure updates

`failSourceMirrorSyncJob` must only update the job, mirror, and source rows when it still holds the lease (`UpdateLeasedJob` succeeds). If the lease was lost — because the lease owner is empty or `UpdateLeasedJob` returns `!ok` — another worker may have already reclaimed and started a new attempt, or the job may have been cancelled by a disable/delete. In that case the function must exit immediately without touching the job, mirror, or source rows, so a stale worker's failure does not overwrite a cancellation or a new attempt.

### Mirror worker observability

Mirror jobs persist a trigger (`manual` or `scheduled`) and a trace id when they are enqueued. The worker records the failed stage, error class, retryability, and bounded Git retry counters in the job's known progress payload. These values are diagnostics, not secrets or raw command output.

The scheduler and worker emit structured lifecycle logs for `enqueued`, `started`, `stage_changed`, `retrying`, `succeeded`, and `failed`. Each record should include `jobId`, `workspaceId`, `kbId`, `sourceId`, `stage`, worker attempt, elapsed time, trigger, and trace id; retry and terminal records may add the safe error class and Git attempt counters. Log messages and persisted errors must use the same display sanitizer and must not include credentials, secret-bearing remote URLs, or raw clone output.

Scheduler enqueue failures, worker job failures, and failure-state persistence errors must be returned to the periodic worker boundary instead of being silently discarded. Batch execution may continue processing independent sources, but it must return an aggregate error after the batch so the server-level worker log and alerting path can observe partial failure. If writing the terminal failure state also fails, the returned error must retain both the original job failure and the persistence failure.

## 8. UI Contract

### Import dialog

After the user submits a repository import:

1. The dialog shows local pending state while the source create request runs.
2. The create flow waits only for source creation and background job enqueue confirmation.
3. After enqueue succeeds, the dialog closes and routes to the source detail surface.
4. The source detail surface shows durable progress and continues polling while work is active.

The dialog must not stay open waiting for long-running mirror sync or repository knowledge generation to finish.
Cache invalidation and follow-up query refetches are also background refresh work; they must not extend the dialog's local pending state after source creation and job enqueue succeed.

### Source list and overview

Source rows should show compact progress:

- active badge: queued, validating remote, syncing repository, generating knowledge, indexing;
- terminal success: ready/current when the source is usable;
- terminal warning: ready with warnings when the source is usable but optional processing failed or produced partial results;
- terminal failure: failed badge and a link or action to open source detail.

The KB overview may aggregate source progress into source health and alerts, but source detail remains the canonical surface for per-source progress details and retry actions.

The KB overview activity stream is the canonical default surface for recent worker failure summaries. A failed mirror activity row may include a sanitized third-line failure summary and must link directly to the corresponding source settings sync-history entry using its job id; source rows should keep those details out of the compact list.

### Source detail

Source detail should include a progress card near the header or settings summary. The card should show:

- current phase and stage label;
- generic active-state description when work is queued or running;
- coarse progress indicator when `percent` is present;
- last updated time;
- retry action when a failed or warning state can be retried;
- an expandable checklist of source import readiness steps;
- a link to diagnostics or recent jobs.

The collapsed card should answer the primary state in one line: importing, ready, ready with warnings, or failed. The expanded checklist should explain how that state was reached. It should show completed required steps with a success affordance, failed required steps with an error affordance, optional failures with a warning affordance, running steps with an activity affordance, pending steps neutrally, and skipped optional steps as not applicable.

Checklist copy should describe product milestones, not internal worker mechanics. For example, show "Generate repository knowledge" rather than the concrete binary or SDK used to perform analysis.

The settings diagnostics area should list recent jobs and analysis runs with status, stage, page count when relevant, and created/updated/finished times. Generic source diagnostics must not echo worker failure messages or errors. Mirror settings additionally own the explicit sync-attempt history described above: selecting an attempt may reveal its sanitized failure reason, error class, localized remediation guidance, trace id, and retry action.

## 9. Polling And Cache Invalidation

The browser should poll only while progress is active or failure recovery is pending.

Recommended intervals:

- source detail: 2-3 seconds while `state` is `queued` or `running`;
- source list / KB overview: 5-10 seconds while any visible source has active progress;
- no polling after all visible progress is terminal or idle.

Starting a mirror sync or repository knowledge generation should invalidate:

- source detail query for the source;
- source mirror sync-history query for the source;
- KB detail/source list query;
- KB overview query when overview health or alerts depend on source progress.

When a mirror sync job succeeds, MeshX must enqueue KB asset access-change
notifications for agents with explicit access to the affected KB so daemon-local
KB caches refresh promptly through the standard agent asset refresh pipeline.

## 10. Retry And Failure Semantics

Failures must preserve enough state for the user or operator to retry safely.

- Mirror sync retry uses the mirror sync request endpoint and the source's configured remote/default ref unless the user supplies a different ref.
- Repository knowledge retry uses the source repository knowledge generation endpoint and the current source repository ref unless the user supplies a different ref.
- Optional analysis or generated-knowledge retry should retry only that optional operation when the source repository is already ready, unless a required upstream sync or repository state also needs repair.
- Retry actions require the same edit/manage permission as the underlying mutation.
- A retry should create or reuse an active source-scoped job according to the job idempotency rules for that job type.
- Failed progress remains visible until a retry starts, the source configuration changes in a way that clears the failure, or a later successful operation supersedes it.
- Warning progress remains visible until a retry starts, the source configuration disables or skips that optional step, or a later successful optional operation supersedes it.

## 11. Completion Visibility And Dismissal

Completed progress may remain visible on the source detail page when it communicates source health: ready, ready with warnings, or failed. Source list and overview surfaces should keep terminal progress compact and avoid permanent high-noise banners for successful imports.

V1 does not require a dismiss action. A warning or failure represents source health, so dismissing it must not mutate the underlying source, job, mirror, analysis, or revision state.

If dismissal is added later, it should be modeled as a per-principal acknowledgement of a source-scoped issue fingerprint, not as workflow state. The fingerprint should be derived from stable facts such as `source_id`, step id, phase/stage, operation id or revision, and sanitized error class. Acknowledgement may hide or collapse a banner for that principal, but diagnostics and source health state must remain available.

## 12. Permissions And Privacy

Progress visibility follows KB source visibility. A principal that cannot view a source must not learn progress about that source through list, overview, detail, polling, or diagnostics APIs.

Mutation controls remain unchanged:

- source creation requires KB source edit/manage permission;
- mirror sync requires source edit/manage permission;
- repository knowledge generation requires source edit/manage permission and repository readability for the selected source repository.

Progress payloads must not expose secret material. Remote URLs must use the sanitized display form already stored on the source mirror. Credential refs are backend-only.

## 13. Implementation Guidance

A durable implementation should:

1. Parse known `knowledge_base_jobs.progress_json` payloads server-side and expose display-safe `stage`, `pageCount`, and `finishedAt` on source detail job diagnostics.
2. Add `importProgress` to source detail using active/latest source-scoped jobs and source/mirror/analysis error state.
3. Add `importProgress` to source list rows when the list UI needs compact progress without opening detail.
4. Add derived `severity` and checklist `steps` using durable source, repository mapping, mirror, analysis, revision, and job state.
5. Add source detail progress card with active polling and expandable checklist details.
6. Add source row badge, overview invalidation, localized labels, warning/failure state copy, and retry actions.
7. Project recent failed source jobs into KB overview activity with sanitized failure summaries.

This path does not require a schema migration because the durable job, mirror, analysis, source, and revision rows already exist. A schema change is only needed when provisioning itself becomes asynchronous or when workers start recording measured progress beyond stage-level hints.

## 14. Non-goals

This spec does not require:

- exposing raw Git clone progress, worker logs, or stack traces;
- exact byte/file percentages for mirror sync;
- keeping the import dialog open until background work completes;
- moving all source creation into a background worker;
- making the checklist a separate durable workflow system;
- adding dismiss or acknowledgement storage in v1;
- changing AGS repository ownership, source lifecycle, or KB permission semantics.
