V13-FE-011: finalize search list layout slice

This commit is contained in:
2026-08-09 02:57:26 +09:00
parent 9efd202e76
commit 6422cb2b13
984 changed files with 120811 additions and 1498 deletions
@@ -0,0 +1,65 @@
export type KbxAiCapability = 'explain' | 'suggest' | 'draft' | 'execute'
export interface KbxAiScreenContext {
screenId: string
screenVersion: string
entityId?: string
selectedIds?: string[]
filters?: Record<string, unknown>
allowedCapabilities: KbxAiCapability[]
}
export interface KbxAiEvidence {
label: string
sourceType: 'domain' | 'api' | 'audit' | 'document' | 'rule'
reference?: string
}
export interface KbxAiProposalChange {
field: string
label: string
before?: unknown
after?: unknown
}
export type KbxAiProposalValidationState = 'pending' | 'validated' | 'invalid' | 'stale'
export interface KbxAiProposalValidation {
state: KbxAiProposalValidationState
message?: string
validatedAt?: string
}
export interface KbxAiProposal {
id: string
type: string
title: string
explanation: string
capability: Exclude<KbxAiCapability, 'explain'>
targets?: { entityType: string; entityId: string }[]
proposedChanges?: KbxAiProposalChange[]
evidence?: KbxAiEvidence[]
confidence?: number
requiredPermission?: string
validation?: KbxAiProposalValidation
}
export interface KbxAiAskRequest {
question: string
context: KbxAiScreenContext
}
export interface KbxAiAnswerAction {
id: string
label: string
kind: 'navigate' | 'filter' | 'proposal'
requiredCapability?: KbxAiCapability
requiredPermission?: string
}
export interface KbxAiAnswer {
answer: string
actions?: KbxAiAnswerAction[]
proposal?: KbxAiProposal
evidence?: KbxAiEvidence[]
}
@@ -0,0 +1,24 @@
export type KbxHttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
export type KbxApiOperationKind = 'query' | 'command' | 'upload'
export type KbxIdempotencyPolicy = 'none' | 'supported' | 'required'
export interface KbxApiOperationDefinition {
id: string
method: KbxHttpMethod
path: string
permission?: string | null
kind: KbxApiOperationKind
idempotency: KbxIdempotencyPolicy
successStatuses: readonly number[]
contentType?: string
responseType?: 'json' | 'blob'
}
export interface KbxApiRequestOptions<TBody = unknown> {
path?: Record<string, string | number>
query?: object
body?: TBody
headers?: Record<string, string>
idempotencyKey?: string
responseType?: 'json' | 'blob'
}
@@ -0,0 +1,18 @@
export type KbxAuditSource = 'user' | 'system' | 'api' | 'import' | 'ai' | 'batch'
export interface KbxAuditEntry {
id: string
occurredAt: string
actor: {
type: KbxAuditSource
displayName: string
}
action: string
changes?: Array<{
field: string
label: string
before?: unknown
after?: unknown
}>
reason?: string
}
@@ -0,0 +1,33 @@
export type KbxPermissionRisk = 'low' | 'medium' | 'high'
export type KbxPermissionPresentation = 'hide' | 'disable'
export interface KbxPermissionDefinition {
id: string
module: 'OMS' | 'ERP' | 'WMS' | 'COMMON'
resource: string
action: string
risk: KbxPermissionRisk
description: string
presentation: KbxPermissionPresentation
backendRequired: boolean
}
export interface KbxSensitiveDataPolicy {
id: string
fields: string[]
defaultExposure: 'masked'
viewPermission: string
revealPermission: string
unmaskedExportPermission: string
auditRequired: boolean
aiExposure: 'masked-only' | 'deny'
telemetryExposure: 'never'
reasonRequired: boolean
}
export interface KbxAuthorizationDecision {
allowed: boolean
permission: string
presentation: KbxPermissionPresentation
reason?: string
}
@@ -0,0 +1,37 @@
import type { KbxDensity } from './preference'
export type KbxCatalogState =
| 'default'
| 'idle'
| 'ready'
| 'refreshing'
| 'readonly'
| 'disabled'
| 'required'
| 'error'
| 'loading'
| 'keyboard'
| 'changed'
| 'warning'
| 'ai-suggested'
| 'empty'
export interface KbxCatalogScenario {
id: string
label: string
state: KbxCatalogState
density?: KbxDensity
description?: string
}
export interface KbxComponentCatalogEntry {
component: string
group: 'input' | 'command' | 'grid' | 'feedback' | 'overlay' | 'template' | 'wms' | 'shell'
scenarios: KbxCatalogScenario[]
accessibility: {
keyboard: boolean
focusVisible: boolean
labelRequired?: boolean
colorIndependentStatus?: boolean
}
}
@@ -0,0 +1,26 @@
export type KbxCommandGroup = 'query' | 'edit' | 'workflow' | 'output' | 'more'
export type KbxCommandVariant = 'primary' | 'secondary' | 'danger' | 'ghost'
export interface KbxCommandConfirmDefinition { title:string; detail:string; level?:'low'|'medium'|'high'; confirmLabel?:string }
export interface KbxCommandDefinition {
id: string
label: string
group: KbxCommandGroup
variant?: KbxCommandVariant
shortcut?: string
permission?: string
/** Optional state-specific authorization for one visual command (e.g. create vs update save). */
permissionByStatus?: Record<string, string>
icon?: string
requiresSelection?: boolean
minSelection?: number
maxSelection?: number
/** Keep the command visible but disabled outside these business states. */
allowedStatuses?: string[]
/** Save-like commands can be disabled until the page is dirty. */
requiresDirty?: boolean
/** Workflow commits can require the current record to be saved first. */
requiresClean?: boolean
disabledReason?: string
confirm?: KbxCommandConfirmDefinition
}
@@ -0,0 +1,34 @@
export type KbxConfigurationValueType =
| 'string'
| 'boolean'
| 'integer'
| 'enum'
| 'uri'
| 'connection-string'
export interface KbxConfigurationSettingDefinition {
key: string
env: string
category: string
type: KbxConfigurationValueType
allowedValues?: readonly string[]
default?: string | number | boolean
minimum?: number
maximum?: number
secret: boolean
restartRequired: boolean
requiredIn?: readonly string[]
requiredWhen?: { key: string; equals: string | number | boolean }
allowedSources?: readonly string[]
}
export interface KbxEnvironmentProfileDefinition {
id: 'Development' | 'Test' | 'Staging' | 'Production'
artifactPromotion: boolean
secretSources: readonly string[]
migrationStrategy: string
providerNetwork: 'forbidden' | 'opt-in'
requireHttps: boolean
providerEndpointOverrideAllowed: boolean
requiredGates: readonly string[]
}
@@ -0,0 +1,151 @@
import { getKbxField, type KbxKnownFieldKey } from './generated/fieldCatalog'
export type KbxImportStatus =
| 'created'
| 'uploaded'
| 'mapping-required'
| 'validating'
| 'validated'
| 'committing'
| 'completed'
| 'partially-completed'
| 'failed'
| 'cancelled'
export type KbxImportMappingSource = 'exact' | 'alias' | 'saved' | 'ai' | 'manual'
export interface KbxImportFieldDefinition {
key: string
label: string
aliases?: string[]
dataType:
| 'text'
| 'code'
| 'integer'
| 'decimal'
| 'quantity'
| 'money'
| 'date'
| 'datetime'
| 'boolean'
| 'lookup'
required?: boolean
maxLength?: number
precision?: number
scale?: number
lookupEntity?: string
importable?: boolean
exportable?: boolean
helpText?: string
sensitive?: boolean
masking?: 'name' | 'phone' | 'address' | 'partial'
}
export interface KbxImportDefinition {
id: string
screenId: string
entity: string
title: string
fields: KbxImportFieldDefinition[]
allowCreate: boolean
allowUpdate: boolean
maxFileSizeBytes?: number
maxRows?: number
}
export interface KbxImportMapping {
sourceColumn: string
targetField: string | null
source: KbxImportMappingSource
confidence?: number
reason?: string
}
export interface KbxImportValidationError {
rowNumber: number
field?: string
sourceColumn?: string
code: string
message: string
severity: 'error' | 'warning'
}
export interface KbxImportFailure {
code: string
title: string
detail?: string
recoverable?: boolean
}
export interface KbxImportSession {
id: string
importType: string
screenId: string
fileName: string
status: KbxImportStatus
totalRows: number
validRows: number
invalidRows: number
warningRows: number
createdRows: number
updatedRows: number
progressPercent: number
mapping: KbxImportMapping[]
sourceColumns: string[]
errors?: KbxImportValidationError[]
failure?: KbxImportFailure
createdAt: string
completedAt?: string
}
export interface KbxImportProgressEvent {
sessionId: string
status: KbxImportStatus
progressPercent: number
totalRows: number
processedRows: number
validRows: number
invalidRows: number
warningRows: number
message?: string
}
export interface KbxSavedImportMapping {
id: string
importType: string
name: string
mappings: KbxImportMapping[]
updatedAt: string
}
/**
* Materialize Excel metadata from the canonical KBX Field Dictionary.
* Screen/import definitions may select fields, but should not duplicate label/type/length/alias metadata.
*/
export function kbxImportField(
key: KbxKnownFieldKey,
overrides: Partial<KbxImportFieldDefinition> = {},
): KbxImportFieldDefinition {
const field = getKbxField(key) as any
if (!field.importable && overrides.importable !== true) {
throw new Error(`${key} is not importable in the KBX field dictionary.`)
}
return {
key,
label: field.label,
aliases: field.aliases ?? [],
dataType: field.dataType,
required: Boolean(field.required),
maxLength: field.maxLength,
precision: field.precision,
scale: field.scale,
lookupEntity: field.lookupEntity,
importable: Boolean(field.importable),
exportable: Boolean(field.exportable),
helpText: field.helpText,
sensitive: Boolean(field.sensitive),
masking: field.masking,
...overrides,
}
}
@@ -0,0 +1,36 @@
export type KbxExperimentRuntimeState = 'draft' | 'running' | 'paused' | 'rolled-back' | 'graduated' | 'completed'
export interface KbxExperimentAssignment {
experimentId: string
flagId: string
screenId: string
enrolled: boolean
variant: string
state: KbxExperimentRuntimeState
}
export interface KbxExperimentAssignmentsResponse { assignments: KbxExperimentAssignment[] }
export interface KbxExperimentVariantMetric {
variant: string
exposures: number
taskCompletions: number
interactionsPerTask?: number | null
taskCompletionP95Ms?: number | null
validationFailureRate?: number | null
}
export interface KbxExperimentOverviewRow {
experimentId: string
screenId: string
state: KbxExperimentRuntimeState
rolloutPercent: number
killSwitch: boolean
decision: 'inconclusive' | 'guardrail-breach' | 'candidate' | 'neutral' | 'rolled-back'
variants: KbxExperimentVariantMetric[]
updatedAt?: string | null
}
export interface KbxExperimentOverviewResponse { items: KbxExperimentOverviewRow[] }
export interface KbxExperimentRolloutRequest { rolloutPercent: number; state: 'running' | 'paused'; reason: string }
export interface KbxExperimentRollbackRequest { reason: string }
@@ -0,0 +1,67 @@
export type KbxExternalDataFreshnessMode = 'strict' | 'stale-while-revalidate' | 'provider-defined'
export type KbxExternalDataState = 'fresh' | 'stale' | 'expired' | 'unavailable'
export type KbxRawSnapshotRetention = 'none' | 'hash-only' | 'encrypted-raw'
export interface KbxExternalDataDatasetDefinition {
id: string
providerId: string
providerOperationId: string
canonicalType: string
normalizer: string
normalizerVersion: string
cacheKeyFields: readonly string[]
freshness: {
mode: KbxExternalDataFreshnessMode
freshForSeconds: number | null
maxStaleSeconds: number | null
backgroundRefresh: boolean
requireExplicitServicePolicy?: boolean
}
snapshot: {
rawRetention: KbxRawSnapshotRetention
normalizedRetentionDays: number
}
ui: {
sourceLabel: string
showProviderObservedAt: boolean
showReceivedAt: boolean
}
}
export interface KbxExternalDataProvenance {
datasetId: string
providerId: string
providerOperationId: string
sourceLabel: string
state: KbxExternalDataState
providerObservedAt?: string | null
requestedAt: string
receivedAt: string
ingestedAt: string
freshUntil?: string | null
usableUntil?: string | null
payloadSha256: string
normalizerVersion: string
cacheHit: boolean
refreshInProgress?: boolean
warning?: string
}
export interface KbxExternalDataEnvelope<T> {
data: T | null
provenance: KbxExternalDataProvenance
}
export interface KbxExternalDataStatusRow {
datasetId: string
providerId: string
sourceLabel: string
state: KbxExternalDataState
cacheEntries: number
lastReceivedAt?: string | null
oldestFreshUntil?: string | null
staleEntries: number
unavailableEntries: number
}
export interface KbxExternalDataStatusResponse { items: KbxExternalDataStatusRow[] }
@@ -0,0 +1,37 @@
import type { KbxLookupDefinition } from './lookup'
import type { KbxKnownFieldKey } from './generated/fieldCatalog'
export type KbxFieldDataType =
| 'text'
| 'code'
| 'integer'
| 'decimal'
| 'quantity'
| 'money'
| 'date'
| 'datetime'
| 'boolean'
| 'lookup'
| 'status'
export interface KbxFieldDefinition<T = unknown> {
/** Canonical fields should use KbxKnownFieldKey. string remains for local/read-model fields during staged adoption. */
key: KbxKnownFieldKey | (string & {})
label: string
aliases?: string[]
dataType: KbxFieldDataType
required?: boolean
maxLength?: number
precision?: number
scale?: number
readonly?: boolean
importable?: boolean
exportable?: boolean
sensitive?: boolean
masking?: 'name' | 'phone' | 'address' | 'partial'
deprecated?: boolean
replacementKey?: KbxKnownFieldKey
defaultValue?: T
lookup?: KbxLookupDefinition
helpText?: string
}
@@ -0,0 +1,75 @@
// Generated from contracts/api/kbx.api.json. Do not edit.
import type { KbxApiOperationDefinition } from '../api'
export const kbxApiSourceSha256 = '8a8008b0eca039ef22b703b142dd7fa2efeaf5b14784ba9f3a954f656a1a0c8f' as const
export const kbxApiCatalog = {
"common.ai.ask": {"id":"common.ai.ask","method":"POST","path":"/api/common/ai/ask","permission":"common.ai.use","kind":"command","idempotency":"supported","successStatuses":[200]},
"common.experiments.assignments": {"id":"common.experiments.assignments","method":"GET","path":"/api/kbx/experiments/assignments","permission":"common.experiment.evaluate","kind":"query","idempotency":"none","successStatuses":[200]},
"common.experiments.overview": {"id":"common.experiments.overview","method":"GET","path":"/api/kbx/experiments","permission":"common.experiment.read","kind":"query","idempotency":"none","successStatuses":[200]},
"common.experiments.rollback": {"id":"common.experiments.rollback","method":"POST","path":"/api/kbx/experiments/{experimentId}/rollback","permission":"common.experiment.manage","kind":"command","idempotency":"supported","successStatuses":[200]},
"common.experiments.rollout": {"id":"common.experiments.rollout","method":"POST","path":"/api/kbx/experiments/{experimentId}/rollout","permission":"common.experiment.manage","kind":"command","idempotency":"supported","successStatuses":[200]},
"common.externalData.refresh": {"id":"common.externalData.refresh","method":"POST","path":"/api/kbx/external-data/{datasetId}/refresh","permission":"common.external-data.refresh","kind":"command","idempotency":"supported","successStatuses":[202]},
"common.externalData.status": {"id":"common.externalData.status","method":"GET","path":"/api/kbx/external-data/status","permission":"common.external-data.read","kind":"query","idempotency":"none","successStatuses":[200]},
"common.imports.commit": {"id":"common.imports.commit","method":"POST","path":"/api/imports/sessions/{sessionId:guid}/commit","permission":"imports.execute","kind":"command","idempotency":"supported","successStatuses":[200]},
"common.imports.createSession": {"id":"common.imports.createSession","method":"POST","path":"/api/imports/sessions","permission":"imports.execute","kind":"upload","idempotency":"supported","successStatuses":[200],"contentType":"multipart/form-data"},
"common.imports.errorWorkbook": {"id":"common.imports.errorWorkbook","method":"GET","path":"/api/imports/sessions/{sessionId:guid}/errors.xlsx","permission":"imports.execute","kind":"query","idempotency":"none","successStatuses":[200],"responseType":"blob"},
"common.imports.getSession": {"id":"common.imports.getSession","method":"GET","path":"/api/imports/sessions/{sessionId:guid}","permission":"imports.execute","kind":"query","idempotency":"none","successStatuses":[200]},
"common.imports.saveMapping": {"id":"common.imports.saveMapping","method":"PUT","path":"/api/imports/sessions/{sessionId:guid}/mapping","permission":"imports.execute","kind":"command","idempotency":"supported","successStatuses":[200]},
"common.imports.saveNamedMapping": {"id":"common.imports.saveNamedMapping","method":"POST","path":"/api/imports/sessions/{sessionId:guid}/saved-mappings","permission":"imports.execute","kind":"command","idempotency":"supported","successStatuses":[204]},
"common.imports.template": {"id":"common.imports.template","method":"GET","path":"/api/imports/{importType}/template","permission":"imports.execute","kind":"query","idempotency":"none","successStatuses":[200],"responseType":"blob"},
"common.imports.validate": {"id":"common.imports.validate","method":"POST","path":"/api/imports/sessions/{sessionId:guid}/validate","permission":"imports.execute","kind":"command","idempotency":"supported","successStatuses":[200]},
"common.integrations.getAttempt": {"id":"common.integrations.getAttempt","method":"GET","path":"/api/integrations/attempts/{attemptId:guid}","permission":"common.integration.read","kind":"query","idempotency":"none","successStatuses":[200]},
"common.integrations.retryAttempt": {"id":"common.integrations.retryAttempt","method":"POST","path":"/api/integrations/attempts/{attemptId:guid}/retry","permission":"common.integration.retry","kind":"command","idempotency":"required","successStatuses":[200]},
"common.operations.claim": {"id":"common.operations.claim","method":"POST","path":"/api/operations/work-items/claim","permission":"common.operations.claim","kind":"command","idempotency":"supported","successStatuses":[200]},
"common.operations.resolve": {"id":"common.operations.resolve","method":"POST","path":"/api/operations/work-items/resolve","permission":"common.operations.resolve","kind":"command","idempotency":"supported","successStatuses":[200]},
"common.operations.retry": {"id":"common.operations.retry","method":"POST","path":"/api/operations/work-items/{id:guid}/retry","permission":"common.operations.retry","kind":"command","idempotency":"supported","successStatuses":[200]},
"common.operations.search": {"id":"common.operations.search","method":"GET","path":"/api/operations/work-items","permission":"common.operations.read","kind":"query","idempotency":"none","successStatuses":[200]},
"common.reconcile.createExceptions": {"id":"common.reconcile.createExceptions","method":"POST","path":"/api/reconcile/items/create-exceptions","permission":"common.operations.create","kind":"command","idempotency":"supported","successStatuses":[200]},
"common.reconcile.search": {"id":"common.reconcile.search","method":"GET","path":"/api/reconcile/items","permission":"common.reconcile.read","kind":"query","idempotency":"none","successStatuses":[200]},
"common.runtime.notice": {"id":"common.runtime.notice","method":"GET","path":"/api/kbx/runtime/notice","permission":"common.runtime.read","kind":"query","idempotency":"none","successStatuses":[200]},
"common.runtime.notificationRead": {"id":"common.runtime.notificationRead","method":"POST","path":"/api/kbx/runtime/notifications/{Id}/read","permission":"common.runtime.read","kind":"command","idempotency":"supported","successStatuses":[204]},
"common.runtime.notifications": {"id":"common.runtime.notifications","method":"GET","path":"/api/kbx/runtime/notifications","permission":"common.runtime.read","kind":"query","idempotency":"none","successStatuses":[200]},
"common.runtime.operations": {"id":"common.runtime.operations","method":"GET","path":"/api/kbx/runtime/operations","permission":"common.runtime.read","kind":"query","idempotency":"none","successStatuses":[200]},
"common.suggestions.submit": {"id":"common.suggestions.submit","method":"POST","path":"/api/common/suggestions","permission":"common.suggestion.create","kind":"command","idempotency":"supported","successStatuses":[200]},
"common.uxTelemetry.ingest": {"id":"common.uxTelemetry.ingest","method":"POST","path":"/api/kbx/ux/events","permission":"common.telemetry.write","kind":"command","idempotency":"none","successStatuses":[204]},
"common.uxTelemetry.metrics": {"id":"common.uxTelemetry.metrics","method":"GET","path":"/api/kbx/ux/metrics","permission":"common.ux.read","kind":"query","idempotency":"none","successStatuses":[200]},
"erp.inventory.history": {"id":"erp.inventory.history","method":"GET","path":"/api/erp/inventory/{itemId}/history","permission":"erp.inventory.read","kind":"query","idempotency":"none","successStatuses":[200]},
"erp.inventory.locations": {"id":"erp.inventory.locations","method":"GET","path":"/api/erp/inventory/{itemId}/locations","permission":"erp.inventory.read","kind":"query","idempotency":"none","successStatuses":[200]},
"erp.inventory.search": {"id":"erp.inventory.search","method":"GET","path":"/api/erp/inventory","permission":"erp.inventory.read","kind":"query","idempotency":"none","successStatuses":[200]},
"erp.itemPrices.bulkSave": {"id":"erp.itemPrices.bulkSave","method":"POST","path":"/api/erp/item-prices/bulk","permission":"erp.item.price.write","kind":"command","idempotency":"required","successStatuses":[200]},
"erp.items.get": {"id":"erp.items.get","method":"GET","path":"/api/erp/items/{id}","permission":"erp.item.read","kind":"query","idempotency":"none","successStatuses":[200]},
"erp.items.search": {"id":"erp.items.search","method":"GET","path":"/api/erp/items","permission":"erp.item.read","kind":"query","idempotency":"none","successStatuses":[200]},
"lookup.customers.resolveByCode": {"id":"lookup.customers.resolveByCode","method":"GET","path":"/api/lookups/customers/by-code/{code}","permission":"oms.order.read","kind":"query","idempotency":"none","successStatuses":[200]},
"lookup.customers.resolveById": {"id":"lookup.customers.resolveById","method":"GET","path":"/api/lookups/customers/{id}","permission":"oms.order.read","kind":"query","idempotency":"none","successStatuses":[200]},
"lookup.customers.search": {"id":"lookup.customers.search","method":"GET","path":"/api/lookups/customers","permission":"oms.order.read","kind":"query","idempotency":"none","successStatuses":[200]},
"lookup.items.resolveByCode": {"id":"lookup.items.resolveByCode","method":"GET","path":"/api/lookups/items/by-code/{code}","permission":"erp.item.read","kind":"query","idempotency":"none","successStatuses":[200]},
"lookup.items.resolveById": {"id":"lookup.items.resolveById","method":"GET","path":"/api/lookups/items/{id}","permission":"erp.item.read","kind":"query","idempotency":"none","successStatuses":[200]},
"lookup.items.search": {"id":"lookup.items.search","method":"GET","path":"/api/lookups/items","permission":"erp.item.read","kind":"query","idempotency":"none","successStatuses":[200]},
"lookup.warehouses.resolveByCode": {"id":"lookup.warehouses.resolveByCode","method":"GET","path":"/api/lookups/warehouses/by-code/{code}","permission":"oms.order.read","kind":"query","idempotency":"none","successStatuses":[200]},
"lookup.warehouses.resolveById": {"id":"lookup.warehouses.resolveById","method":"GET","path":"/api/lookups/warehouses/{id}","permission":"oms.order.read","kind":"query","idempotency":"none","successStatuses":[200]},
"lookup.warehouses.search": {"id":"lookup.warehouses.search","method":"GET","path":"/api/lookups/warehouses","permission":"oms.order.read","kind":"query","idempotency":"none","successStatuses":[200]},
"oms.claims.approve": {"id":"oms.claims.approve","method":"POST","path":"/api/oms/claims/{id:guid}/approve","permission":"oms.claim.approve","kind":"command","idempotency":"supported","successStatuses":[200]},
"oms.claims.complete": {"id":"oms.claims.complete","method":"POST","path":"/api/oms/claims/{id:guid}/complete","permission":"oms.claim.process","kind":"command","idempotency":"supported","successStatuses":[200]},
"oms.claims.hold": {"id":"oms.claims.hold","method":"POST","path":"/api/oms/claims/{id:guid}/hold","permission":"oms.claim.hold","kind":"command","idempotency":"supported","successStatuses":[200]},
"oms.claims.search": {"id":"oms.claims.search","method":"GET","path":"/api/oms/claims","permission":"oms.claim.read","kind":"query","idempotency":"none","successStatuses":[200]},
"oms.claims.start": {"id":"oms.claims.start","method":"POST","path":"/api/oms/claims/{id:guid}/start","permission":"oms.claim.process","kind":"command","idempotency":"supported","successStatuses":[200]},
"oms.orders.register": {"id":"oms.orders.register","method":"POST","path":"/api/oms/orders/register","permission":"oms.order.create","kind":"command","idempotency":"supported","successStatuses":[200]},
"oms.orders.search": {"id":"oms.orders.search","method":"GET","path":"/api/oms/orders","permission":"oms.order.read","kind":"query","idempotency":"none","successStatuses":[200]},
"oms.orders.ship": {"id":"oms.orders.ship","method":"POST","path":"/api/oms/orders/ship","permission":"oms.order.ship","kind":"command","idempotency":"required","successStatuses":[200]},
"wms.picking.getTask": {"id":"wms.picking.getTask","method":"GET","path":"/api/wms/picking/tasks/{taskId:guid}","permission":"wms.picking.execute","kind":"query","idempotency":"none","successStatuses":[200]},
"wms.picking.reportException": {"id":"wms.picking.reportException","method":"POST","path":"/api/wms/picking/tasks/{taskId:guid}/exceptions","permission":"wms.picking.execute","kind":"command","idempotency":"required","successStatuses":[200]},
"wms.picking.scan": {"id":"wms.picking.scan","method":"POST","path":"/api/wms/picking/tasks/{taskId:guid}/scan","permission":"wms.picking.execute","kind":"command","idempotency":"required","successStatuses":[200,422]},
"wms.picking.setQuantity": {"id":"wms.picking.setQuantity","method":"POST","path":"/api/wms/picking/tasks/{taskId:guid}/quantity","permission":"wms.picking.execute","kind":"command","idempotency":"required","successStatuses":[200]},
"wms.picking.start": {"id":"wms.picking.start","method":"POST","path":"/api/wms/picking/tasks/{taskId:guid}/start","permission":"wms.picking.execute","kind":"command","idempotency":"supported","successStatuses":[200]},
"erp.items.create": {"id":"erp.items.create","method":"POST","path":"/api/erp/items","permission":"erp.item.create","kind":"command","idempotency":"none","successStatuses":[200]},
"erp.items.update": {"id":"erp.items.update","method":"PUT","path":"/api/erp/items/{id}","permission":"erp.item.write","kind":"command","idempotency":"none","successStatuses":[200]},
"erp.items.deactivate": {"id":"erp.items.deactivate","method":"POST","path":"/api/erp/items/{id}/deactivate","permission":"erp.item.write","kind":"command","idempotency":"none","successStatuses":[200]},
"erp.items.audit": {"id":"erp.items.audit","method":"GET","path":"/api/erp/items/{id}/audit","permission":"erp.item.read","kind":"query","idempotency":"none","successStatuses":[200]},
"oms.orders.get": {"id":"oms.orders.get","method":"GET","path":"/api/oms/orders/{id}","permission":"oms.order.read","kind":"query","idempotency":"none","successStatuses":[200]},
"oms.orders.confirm": {"id":"oms.orders.confirm","method":"POST","path":"/api/oms/orders/{id}/confirm","permission":"oms.order.confirm","kind":"command","idempotency":"required","successStatuses":[200]},
"oms.orders.audit": {"id":"oms.orders.audit","method":"GET","path":"/api/oms/orders/{id}/audit","permission":"oms.order.read","kind":"query","idempotency":"none","successStatuses":[200]},
"oms.orders.update": {"id":"oms.orders.update","method":"PUT","path":"/api/oms/orders/{id}","permission":"oms.order.write","kind":"command","idempotency":"supported","successStatuses":[200]},
} as const satisfies Record<string, KbxApiOperationDefinition>
export type KbxApiOperationId = keyof typeof kbxApiCatalog
export type KbxApiOperation = (typeof kbxApiCatalog)[KbxApiOperationId]
@@ -0,0 +1,506 @@
// generated from contracts/configuration/kbx.configuration.json; do not edit.
import type { KbxConfigurationSettingDefinition, KbxEnvironmentProfileDefinition } from '../configuration'
export const kbxConfigurationSourceSha256='e09186d626b034a524b7cdf78f1e4bb6adbc112975b3baf4fd6194b1c1abd771' as const
export const kbxConfigurationCatalog={
"Kbx:Runtime:Environment": {
"key": "Kbx:Runtime:Environment",
"env": "KBX__Runtime__Environment",
"category": "runtime",
"type": "enum",
"allowedValues": [
"Development",
"Test",
"Staging",
"Production"
],
"default": "Development",
"secret": false,
"restartRequired": true,
"requiredIn": [
"Development",
"Test",
"Staging",
"Production"
]
},
"Kbx:Runtime:ReadOnly": {
"key": "Kbx:Runtime:ReadOnly",
"env": "KBX__Runtime__ReadOnly",
"category": "runtime",
"type": "boolean",
"default": false,
"secret": false,
"restartRequired": false,
"requiredIn": []
},
"ConnectionStrings:Main": {
"key": "ConnectionStrings:Main",
"env": "ConnectionStrings__Main",
"category": "database",
"type": "connection-string",
"secret": true,
"restartRequired": true,
"requiredIn": [
"Development",
"Test",
"Staging",
"Production"
],
"allowedSources": [
"environment",
"secret-store",
"user-secrets"
]
},
"Kbx:Database:MigrationsMode": {
"key": "Kbx:Database:MigrationsMode",
"env": "KBX__Database__MigrationsMode",
"category": "database",
"type": "enum",
"allowedValues": [
"validate",
"startup-apply",
"predeploy"
],
"default": "validate",
"secret": false,
"restartRequired": true,
"requiredIn": [
"Development",
"Test",
"Staging",
"Production"
]
},
"Kbx:Database:CommandTimeoutSeconds": {
"key": "Kbx:Database:CommandTimeoutSeconds",
"env": "KBX__Database__CommandTimeoutSeconds",
"category": "database",
"type": "integer",
"default": 30,
"minimum": 1,
"maximum": 300,
"secret": false,
"restartRequired": true,
"requiredIn": []
},
"Kbx:Hangfire:Enabled": {
"key": "Kbx:Hangfire:Enabled",
"env": "KBX__Hangfire__Enabled",
"category": "background-jobs",
"type": "boolean",
"default": true,
"secret": false,
"restartRequired": true,
"requiredIn": []
},
"Kbx:Hangfire:WorkerCount": {
"key": "Kbx:Hangfire:WorkerCount",
"env": "KBX__Hangfire__WorkerCount",
"category": "background-jobs",
"type": "integer",
"default": 4,
"minimum": 1,
"maximum": 64,
"secret": false,
"restartRequired": true,
"requiredIn": []
},
"Kbx:SignalR:Enabled": {
"key": "Kbx:SignalR:Enabled",
"env": "KBX__SignalR__Enabled",
"category": "realtime",
"type": "boolean",
"default": true,
"secret": false,
"restartRequired": true,
"requiredIn": []
},
"Kbx:Outbox:DispatcherEnabled": {
"key": "Kbx:Outbox:DispatcherEnabled",
"env": "KBX__Outbox__DispatcherEnabled",
"category": "messaging",
"type": "boolean",
"default": true,
"secret": false,
"restartRequired": true,
"requiredIn": []
},
"Kbx:Inbox:CleanupEnabled": {
"key": "Kbx:Inbox:CleanupEnabled",
"env": "KBX__Inbox__CleanupEnabled",
"category": "messaging",
"type": "boolean",
"default": true,
"secret": false,
"restartRequired": true,
"requiredIn": []
},
"Kbx:Integration:DispatcherEnabled": {
"key": "Kbx:Integration:DispatcherEnabled",
"env": "KBX__Integration__DispatcherEnabled",
"category": "integration",
"type": "boolean",
"default": true,
"secret": false,
"restartRequired": true,
"requiredIn": []
},
"Kbx:ExternalData:RefreshEnabled": {
"key": "Kbx:ExternalData:RefreshEnabled",
"env": "KBX__ExternalData__RefreshEnabled",
"category": "external-data",
"type": "boolean",
"default": true,
"secret": false,
"restartRequired": true,
"requiredIn": []
},
"Kbx:ExternalData:ObservationRetentionEnabled": {
"key": "Kbx:ExternalData:ObservationRetentionEnabled",
"env": "KBX__ExternalData__ObservationRetentionEnabled",
"category": "external-data",
"type": "boolean",
"default": true,
"secret": false,
"restartRequired": true,
"requiredIn": []
},
"Kbx:Experiments:Enabled": {
"key": "Kbx:Experiments:Enabled",
"env": "KBX__Experiments__Enabled",
"category": "experiments",
"type": "boolean",
"default": true,
"secret": false,
"restartRequired": false,
"requiredIn": []
},
"Kbx:Logging:MinimumLevel": {
"key": "Kbx:Logging:MinimumLevel",
"env": "KBX__Logging__MinimumLevel",
"category": "logging",
"type": "enum",
"allowedValues": [
"Debug",
"Information",
"Warning",
"Error"
],
"default": "Information",
"secret": false,
"restartRequired": true,
"requiredIn": []
},
"Kbx:Logging:JsonConsoleEnabled": {
"key": "Kbx:Logging:JsonConsoleEnabled",
"env": "KBX__Logging__JsonConsoleEnabled",
"category": "logging",
"type": "boolean",
"default": true,
"secret": false,
"restartRequired": true,
"requiredIn": []
},
"Kbx:Telemetry:Enabled": {
"key": "Kbx:Telemetry:Enabled",
"env": "KBX__Telemetry__Enabled",
"category": "observability",
"type": "boolean",
"default": true,
"secret": false,
"restartRequired": true,
"requiredIn": []
},
"Kbx:Telemetry:OtlpEndpoint": {
"key": "Kbx:Telemetry:OtlpEndpoint",
"env": "KBX__Telemetry__OtlpEndpoint",
"category": "observability",
"type": "uri",
"secret": false,
"restartRequired": true,
"requiredIn": []
},
"Kbx:Telegram:Enabled": {
"key": "Kbx:Telegram:Enabled",
"env": "KBX__Telegram__Enabled",
"category": "alerting",
"type": "boolean",
"default": false,
"secret": false,
"restartRequired": true,
"requiredIn": []
},
"Kbx:Telegram:BotToken": {
"key": "Kbx:Telegram:BotToken",
"env": "KBX__Telegram__BotToken",
"category": "alerting",
"type": "string",
"secret": true,
"restartRequired": true,
"requiredWhen": {
"key": "Kbx:Telegram:Enabled",
"equals": true
},
"allowedSources": [
"environment",
"secret-store",
"user-secrets"
]
},
"Kbx:Telegram:ChatId": {
"key": "Kbx:Telegram:ChatId",
"env": "KBX__Telegram__ChatId",
"category": "alerting",
"type": "string",
"secret": true,
"restartRequired": true,
"requiredWhen": {
"key": "Kbx:Telegram:Enabled",
"equals": true
},
"allowedSources": [
"environment",
"secret-store",
"user-secrets"
]
},
"ExternalProviders:Krx:Enabled": {
"key": "ExternalProviders:Krx:Enabled",
"env": "ExternalProviders__Krx__Enabled",
"category": "provider",
"type": "boolean",
"default": false,
"secret": false,
"restartRequired": true,
"requiredIn": []
},
"ExternalProviders:Krx:AuthKey": {
"key": "ExternalProviders:Krx:AuthKey",
"env": "ExternalProviders__Krx__AuthKey",
"category": "provider",
"type": "string",
"secret": true,
"restartRequired": true,
"requiredWhen": {
"key": "ExternalProviders:Krx:Enabled",
"equals": true
},
"allowedSources": [
"environment",
"secret-store",
"user-secrets"
]
},
"ExternalProviders:OpenDart:Enabled": {
"key": "ExternalProviders:OpenDart:Enabled",
"env": "ExternalProviders__OpenDart__Enabled",
"category": "provider",
"type": "boolean",
"default": false,
"secret": false,
"restartRequired": true,
"requiredIn": []
},
"ExternalProviders:OpenDart:ApiKey": {
"key": "ExternalProviders:OpenDart:ApiKey",
"env": "ExternalProviders__OpenDart__ApiKey",
"category": "provider",
"type": "string",
"secret": true,
"restartRequired": true,
"requiredWhen": {
"key": "ExternalProviders:OpenDart:Enabled",
"equals": true
},
"allowedSources": [
"environment",
"secret-store",
"user-secrets"
]
},
"ExternalProviders:Kis:Enabled": {
"key": "ExternalProviders:Kis:Enabled",
"env": "ExternalProviders__Kis__Enabled",
"category": "provider",
"type": "boolean",
"default": false,
"secret": false,
"restartRequired": true,
"requiredIn": []
},
"ExternalProviders:Kis:Environment": {
"key": "ExternalProviders:Kis:Environment",
"env": "ExternalProviders__Kis__Environment",
"category": "provider",
"type": "enum",
"allowedValues": [
"sandbox",
"production"
],
"default": "sandbox",
"secret": false,
"restartRequired": true,
"requiredWhen": {
"key": "ExternalProviders:Kis:Enabled",
"equals": true
}
},
"ExternalProviders:Kis:AppKey": {
"key": "ExternalProviders:Kis:AppKey",
"env": "ExternalProviders__Kis__AppKey",
"category": "provider",
"type": "string",
"secret": true,
"restartRequired": true,
"requiredWhen": {
"key": "ExternalProviders:Kis:Enabled",
"equals": true
},
"allowedSources": [
"environment",
"secret-store",
"user-secrets"
]
},
"ExternalProviders:Kis:AppSecret": {
"key": "ExternalProviders:Kis:AppSecret",
"env": "ExternalProviders__Kis__AppSecret",
"category": "provider",
"type": "string",
"secret": true,
"restartRequired": true,
"requiredWhen": {
"key": "ExternalProviders:Kis:Enabled",
"equals": true
},
"allowedSources": [
"environment",
"secret-store",
"user-secrets"
]
},
"Kbx:Security:RequireHttps": {
"key": "Kbx:Security:RequireHttps",
"env": "KBX__Security__RequireHttps",
"category": "security",
"type": "boolean",
"default": true,
"secret": false,
"restartRequired": true,
"requiredIn": []
},
"Kbx:Health:ReadinessEnabled": {
"key": "Kbx:Health:ReadinessEnabled",
"env": "KBX__Health__ReadinessEnabled",
"category": "runtime",
"type": "boolean",
"default": true,
"secret": false,
"restartRequired": true,
"requiredIn": []
},
"Kbx:Import:MaxUploadBytes": {
"key": "Kbx:Import:MaxUploadBytes",
"env": "KBX__Import__MaxUploadBytes",
"category": "import",
"type": "integer",
"default": 52428800,
"minimum": 1048576,
"maximum": 209715200,
"secret": false,
"restartRequired": true,
"requiredIn": []
},
"Kbx:Import:MaxRows": {
"key": "Kbx:Import:MaxRows",
"env": "KBX__Import__MaxRows",
"category": "import",
"type": "integer",
"default": 100000,
"minimum": 1,
"maximum": 1000000,
"secret": false,
"restartRequired": true,
"requiredIn": []
}
} as const satisfies Record<string,KbxConfigurationSettingDefinition>
export const kbxEnvironmentProfiles={
"Development": {
"id": "Development",
"artifactPromotion": false,
"secretSources": [
"user-secrets",
"environment"
],
"migrationStrategy": "startup-apply-allowed",
"providerNetwork": "opt-in",
"requireHttps": false,
"providerEndpointOverrideAllowed": true,
"requiredGates": [
"static-governance"
]
},
"Test": {
"id": "Test",
"artifactPromotion": false,
"secretSources": [
"environment"
],
"migrationStrategy": "ephemeral-apply",
"providerNetwork": "forbidden",
"requireHttps": false,
"providerEndpointOverrideAllowed": true,
"requiredGates": [
"static-governance",
"build",
"unit",
"integration",
"scenario"
]
},
"Staging": {
"id": "Staging",
"artifactPromotion": true,
"secretSources": [
"secret-store",
"environment"
],
"migrationStrategy": "predeploy",
"providerNetwork": "opt-in",
"requireHttps": true,
"providerEndpointOverrideAllowed": false,
"requiredGates": [
"static-governance",
"build",
"unit",
"integration",
"scenario",
"migration-dry-run"
]
},
"Production": {
"id": "Production",
"artifactPromotion": true,
"secretSources": [
"secret-store",
"environment"
],
"migrationStrategy": "predeploy",
"providerNetwork": "opt-in",
"requireHttps": true,
"providerEndpointOverrideAllowed": false,
"requiredGates": [
"static-governance",
"build",
"unit",
"integration",
"scenario",
"migration-dry-run",
"configuration-validation",
"release-governance"
]
}
} as const satisfies Record<string,KbxEnvironmentProfileDefinition>
export type KbxConfigurationKey=keyof typeof kbxConfigurationCatalog
export type KbxEnvironmentProfile=keyof typeof kbxEnvironmentProfiles
@@ -0,0 +1,59 @@
// generated from contracts/experiments/kbx.experiments.json; do not edit.
export const kbxFeatureFlags = {
"oms.order-list.exception-summary-v2": {
"id": "oms.order-list.exception-summary-v2",
"screenId": "OMS-ORD-001",
"surface": "information-emphasis",
"description": "기존 주문 Quick Filter를 이동시키지 않고 확인 필요 예외 요약을 보조적으로 노출하는 되돌릴 수 있는 UX 변경",
"defaultVariant": "control",
"killSwitch": true,
"owner": "KBX"
}
} as const
export const kbxExperiments = {
"exp.oms.order-list.exception-summary-v2": {
"id": "exp.oms.order-list.exception-summary-v2",
"flagId": "oms.order-list.exception-summary-v2",
"screenId": "OMS-ORD-001",
"state": "draft",
"rolloutPercent": 0,
"minExposurePerVariant": 200,
"variants": [
{
"key": "control",
"weight": 50
},
{
"key": "exception-summary",
"weight": 50
}
],
"primaryMetric": {
"key": "semantic_interactions_per_task",
"direction": "lower",
"minimumImprovementPercent": 10
},
"guardrails": [
{
"key": "task_completion_time_ms",
"statistic": "p95",
"maxRegressionPercent": 20
},
{
"key": "validation_failure_rate",
"statistic": "rate",
"maxAbsoluteRegressionPercentagePoints": 2
}
],
"globalGuardrails": [
{
"key": "manual_intervention_rate",
"maxAbsoluteRegressionPercentagePoints": 0.5
}
],
"notes": "Reference experiment only. v17 ships at draft/0%; rollout requires an explicit runtime decision and can be killed immediately."
}
} as const
export type KbxFeatureFlagId = keyof typeof kbxFeatureFlags
export type KbxExperimentId = keyof typeof kbxExperiments
export const kbxExperimentSourceSha256 = '2510a2927bab297858e09e75c8af9bb766d91b92cc0daf99433f6377d399fbf9' as const
@@ -0,0 +1,118 @@
// generated from contracts/external-data/kbx.external-data.json; do not edit.
import type { KbxExternalDataDatasetDefinition } from '../externalData'
export const kbxExternalDataSourceSha256='da54ff739c6dc5ed6f9390d1fc8a8e138060d7cf19531a80cc91e421d62954a4' as const
export const kbxExternalDataCatalog={
"dataset.opendart.company-profile": {
"id": "dataset.opendart.company-profile",
"providerId": "provider.opendart",
"providerOperationId": "opendart.company",
"canonicalType": "company-profile",
"normalizer": "OpenDartCompanyProfileNormalizer",
"normalizerVersion": "1.0.0",
"cacheKeyFields": [
"corpCode"
],
"freshness": {
"mode": "stale-while-revalidate",
"freshForSeconds": 86400,
"maxStaleSeconds": 604800,
"backgroundRefresh": true
},
"snapshot": {
"rawRetention": "hash-only",
"normalizedRetentionDays": 365
},
"ui": {
"sourceLabel": "OPENDART",
"showProviderObservedAt": false,
"showReceivedAt": true
}
},
"dataset.opendart.disclosures": {
"id": "dataset.opendart.disclosures",
"providerId": "provider.opendart",
"providerOperationId": "opendart.disclosures",
"canonicalType": "disclosure-list",
"normalizer": "OpenDartDisclosureListNormalizer",
"normalizerVersion": "1.0.0",
"cacheKeyFields": [
"corpCode",
"beginDate",
"endDate",
"pageNo",
"pageCount"
],
"freshness": {
"mode": "stale-while-revalidate",
"freshForSeconds": 300,
"maxStaleSeconds": 3600,
"backgroundRefresh": true
},
"snapshot": {
"rawRetention": "hash-only",
"normalizedRetentionDays": 90
},
"ui": {
"sourceLabel": "OPENDART",
"showProviderObservedAt": false,
"showReceivedAt": true
}
},
"dataset.kis.domestic-stock.current-price": {
"id": "dataset.kis.domestic-stock.current-price",
"providerId": "provider.kis.market-data",
"providerOperationId": "kis.domestic-stock.current-price",
"canonicalType": "market-price",
"normalizer": "KisCurrentPriceNormalizer",
"normalizerVersion": "1.0.0",
"cacheKeyFields": [
"marketDivisionCode",
"stockCode",
"environment"
],
"freshness": {
"mode": "strict",
"freshForSeconds": 3,
"maxStaleSeconds": 0,
"backgroundRefresh": false
},
"snapshot": {
"rawRetention": "hash-only",
"normalizedRetentionDays": 7
},
"ui": {
"sourceLabel": "KIS",
"showProviderObservedAt": false,
"showReceivedAt": true
}
},
"dataset.krx.approved-service": {
"id": "dataset.krx.approved-service",
"providerId": "provider.krx.openapi",
"providerOperationId": "krx.approved-service.invoke",
"canonicalType": "provider-defined",
"normalizer": "registered-per-approved-service",
"normalizerVersion": "provider-defined",
"cacheKeyFields": [
"serviceId",
"querySignature"
],
"freshness": {
"mode": "provider-defined",
"freshForSeconds": null,
"maxStaleSeconds": null,
"backgroundRefresh": false,
"requireExplicitServicePolicy": true
},
"snapshot": {
"rawRetention": "hash-only",
"normalizedRetentionDays": 30
},
"ui": {
"sourceLabel": "KRX",
"showProviderObservedAt": false,
"showReceivedAt": true
}
}
} as const satisfies Record<string,KbxExternalDataDatasetDefinition>
export type KbxExternalDataDatasetId=keyof typeof kbxExternalDataCatalog
@@ -0,0 +1,66 @@
// GENERATED FILE. DO NOT EDIT.
// Source: contracts/fields/kbx.fields.json
// Source SHA256: f76e073cd8052e9fddb25cc73322686c889822a22fab712ff1a25bef29b32e3c
export const kbxFieldDictionaryVersion = "1.1.0" as const
export const kbxFieldCatalog = {
"address1": {"key":"address1","label":"주소","aliases":["기본주소","배송주소","Address1"],"dataType":"text","required":true,"maxLength":500,"importable":true,"exportable":true,"sensitive":true,"masking":"address"},
"address2": {"key":"address2","label":"상세주소","aliases":["주소2","Address2"],"dataType":"text","maxLength":500,"importable":true,"exportable":true,"sensitive":true,"masking":"address"},
"allocatedQty": {"key":"allocatedQty","label":"할당수량","aliases":["AllocatedQty"],"dataType":"quantity","precision":18,"scale":4,"readonly":true,"importable":false,"exportable":true,"sensitive":false},
"amount": {"key":"amount","label":"금액","aliases":["Amount","주문금액"],"dataType":"money","precision":18,"scale":2,"readonly":true,"importable":false,"exportable":true,"sensitive":false},
"availableQty": {"key":"availableQty","label":"가용재고","aliases":["AvailableQty","출고가능수량"],"dataType":"quantity","precision":18,"scale":4,"readonly":true,"importable":false,"exportable":true,"sensitive":false},
"barcode": {"key":"barcode","label":"바코드","aliases":["Barcode","EAN","JAN"],"dataType":"code","maxLength":100,"lookupEntity":"item","importable":true,"exportable":true,"sensitive":false},
"channelId": {"key":"channelId","label":"판매채널ID","aliases":["ChannelId"],"dataType":"lookup","lookupEntity":"salesChannel","readonly":true,"importable":false,"exportable":false,"sensitive":false},
"channelName": {"key":"channelName","label":"판매채널","aliases":["ChannelName","채널"],"dataType":"text","readonly":true,"importable":false,"exportable":true,"sensitive":false},
"createdAt": {"key":"createdAt","label":"등록일시","aliases":["CreatedAt"],"dataType":"datetime","readonly":true,"importable":false,"exportable":true,"sensitive":false},
"createdBy": {"key":"createdBy","label":"등록자","aliases":["CreatedBy"],"dataType":"text","readonly":true,"importable":false,"exportable":true,"sensitive":false},
"customerCode": {"key":"customerCode","label":"거래처코드","aliases":["거래처","업체코드","CustomerCode"],"dataType":"code","required":true,"maxLength":50,"lookupEntity":"customer","importable":true,"exportable":true,"sensitive":false},
"customerId": {"key":"customerId","label":"거래처ID","aliases":["CustomerId"],"dataType":"lookup","lookupEntity":"customer","readonly":true,"importable":false,"exportable":false,"sensitive":false},
"customerName": {"key":"customerName","label":"거래처명","aliases":["CustomerName","업체명"],"dataType":"text","maxLength":200,"readonly":true,"importable":false,"exportable":true,"sensitive":false},
"damagedQty": {"key":"damagedQty","label":"불량수량","aliases":["DamagedQty"],"dataType":"quantity","precision":18,"scale":4,"readonly":true,"importable":false,"exportable":true,"sensitive":false},
"effectiveDate": {"key":"effectiveDate","label":"적용일","aliases":["EffectiveDate","시작일","적용시작일"],"dataType":"date","required":true,"importable":true,"exportable":true,"sensitive":false},
"exceptionCount": {"key":"exceptionCount","label":"예외건수","aliases":["ExceptionCount","오류건수"],"dataType":"integer","readonly":true,"importable":false,"exportable":true,"sensitive":false},
"expiryDate": {"key":"expiryDate","label":"유통기한","aliases":["ExpiryDate","ExpirationDate"],"dataType":"date","importable":true,"exportable":true,"sensitive":false},
"holdQty": {"key":"holdQty","label":"보류수량","aliases":["HoldQty"],"dataType":"quantity","precision":18,"scale":4,"readonly":true,"importable":false,"exportable":true,"sensitive":false},
"itemCode": {"key":"itemCode","label":"품목코드","aliases":["상품코드","SKU","ItemCode"],"dataType":"code","required":true,"maxLength":80,"lookupEntity":"item","importable":true,"exportable":true,"sensitive":false},
"itemId": {"key":"itemId","label":"품목ID","aliases":["ItemId"],"dataType":"lookup","lookupEntity":"item","readonly":true,"importable":false,"exportable":false,"sensitive":false},
"itemName": {"key":"itemName","label":"품목명","aliases":["상품명","ItemName"],"dataType":"text","maxLength":300,"readonly":true,"importable":false,"exportable":true,"sensitive":false},
"itemSummary": {"key":"itemSummary","label":"대표상품","aliases":["ItemSummary"],"dataType":"text","readonly":true,"importable":false,"exportable":true,"sensitive":false},
"locationCode": {"key":"locationCode","label":"로케이션","aliases":["Location","LocationCode","위치"],"dataType":"code","maxLength":80,"lookupEntity":"location","importable":true,"exportable":true,"sensitive":false},
"locationId": {"key":"locationId","label":"로케이션ID","aliases":["LocationId"],"dataType":"lookup","lookupEntity":"location","readonly":true,"importable":false,"exportable":false,"sensitive":false},
"lotNo": {"key":"lotNo","label":"LOT","aliases":["LotNo","LOT번호"],"dataType":"code","maxLength":100,"importable":true,"exportable":true,"sensitive":false},
"occurredAt": {"key":"occurredAt","label":"발생시각","aliases":["OccurredAt"],"dataType":"datetime","readonly":true,"importable":false,"exportable":true,"sensitive":false},
"onHandQty": {"key":"onHandQty","label":"현재고","aliases":["OnHandQty","장부재고"],"dataType":"quantity","precision":18,"scale":4,"readonly":true,"importable":false,"exportable":true,"sensitive":false},
"orderDate": {"key":"orderDate","label":"주문일","aliases":["주문일자","OrderDate"],"dataType":"date","required":true,"importable":true,"exportable":true,"sensitive":false},
"orderedAt": {"key":"orderedAt","label":"주문일시","aliases":["OrderedAt","주문시간"],"dataType":"datetime","readonly":true,"importable":false,"exportable":true,"sensitive":false},
"orderId": {"key":"orderId","label":"주문ID","aliases":["OrderId"],"dataType":"code","readonly":true,"importable":false,"exportable":false,"sensitive":false},
"orderNo": {"key":"orderNo","label":"주문번호","aliases":["OrderNo","주문ID"],"dataType":"code","required":true,"maxLength":50,"importable":true,"exportable":true,"sensitive":false},
"orderQty": {"key":"orderQty","label":"주문수량","aliases":["주문수량","수량","Qty","OrderQty"],"dataType":"quantity","precision":18,"scale":4,"importable":true,"exportable":true,"sensitive":false,"required":true},
"ownerName": {"key":"ownerName","label":"담당자","aliases":["OwnerName","담당"],"dataType":"text","maxLength":100,"readonly":true,"importable":false,"exportable":true,"sensitive":false},
"phone": {"key":"phone","label":"연락처","aliases":["휴대폰","전화번호","Phone"],"dataType":"text","required":true,"maxLength":50,"importable":true,"exportable":true,"sensitive":true,"masking":"phone"},
"pickedQty": {"key":"pickedQty","label":"피킹수량","aliases":["PickedQty"],"dataType":"quantity","precision":18,"scale":4,"readonly":true,"importable":false,"exportable":true,"sensitive":false},
"postalCode": {"key":"postalCode","label":"우편번호","aliases":["ZipCode","PostalCode"],"dataType":"text","maxLength":20,"importable":true,"exportable":true,"sensitive":true,"masking":"address"},
"quantity": {"key":"quantity","label":"수량","aliases":["Qty"],"dataType":"quantity","precision":18,"scale":4,"importable":false,"exportable":false,"sensitive":false},
"receiverName": {"key":"receiverName","label":"수취인","aliases":["받는분","수령인","ReceiverName"],"dataType":"text","required":true,"maxLength":100,"importable":true,"exportable":true,"sensitive":true,"masking":"name"},
"referenceNo": {"key":"referenceNo","label":"참조번호","aliases":["ReferenceNo"],"dataType":"code","readonly":true,"importable":false,"exportable":true,"sensitive":false},
"remark": {"key":"remark","label":"비고","aliases":["메모","배송메모","Remark"],"dataType":"text","maxLength":500,"importable":true,"exportable":true,"sensitive":false},
"shippedQty": {"key":"shippedQty","label":"출고수량","aliases":["ShippedQty"],"dataType":"quantity","precision":18,"scale":4,"readonly":true,"importable":false,"exportable":true,"sensitive":false},
"specification": {"key":"specification","label":"규격","aliases":["Specification","Spec"],"dataType":"text","maxLength":200,"importable":true,"exportable":true,"sensitive":false},
"status": {"key":"status","label":"상태","aliases":["Status"],"dataType":"status","readonly":true,"importable":false,"exportable":true,"sensitive":false},
"totalQty": {"key":"totalQty","label":"총수량","aliases":["TotalQty"],"dataType":"quantity","precision":18,"scale":4,"readonly":true,"importable":false,"exportable":true,"sensitive":false},
"unitPrice": {"key":"unitPrice","label":"단가","aliases":["판매단가","UnitPrice"],"dataType":"money","precision":18,"scale":4,"importable":true,"exportable":true,"sensitive":false},
"updatedAt": {"key":"updatedAt","label":"수정일시","aliases":["UpdatedAt"],"dataType":"datetime","readonly":true,"importable":false,"exportable":true,"sensitive":false},
"updatedBy": {"key":"updatedBy","label":"수정자","aliases":["UpdatedBy"],"dataType":"text","readonly":true,"importable":false,"exportable":true,"sensitive":false},
"version": {"key":"version","label":"버전","aliases":["Version","RowVersion"],"dataType":"integer","readonly":true,"importable":false,"exportable":false,"sensitive":false},
"warehouseCode": {"key":"warehouseCode","label":"창고코드","aliases":["창고","창고코드","출고창고","WarehouseCode"],"dataType":"code","required":true,"maxLength":50,"lookupEntity":"warehouse","importable":true,"exportable":true,"sensitive":false},
"warehouseId": {"key":"warehouseId","label":"창고ID","aliases":["WarehouseId"],"dataType":"lookup","lookupEntity":"warehouse","readonly":true,"importable":false,"exportable":false,"sensitive":false},
"warehouseName": {"key":"warehouseName","label":"창고명","aliases":["WarehouseName"],"dataType":"text","maxLength":200,"readonly":true,"importable":false,"exportable":true,"sensitive":false},
} as const
export type KbxKnownFieldKey = keyof typeof kbxFieldCatalog
export type KbxKnownFieldDefinition = (typeof kbxFieldCatalog)[KbxKnownFieldKey]
export function getKbxField<K extends KbxKnownFieldKey>(key: K): (typeof kbxFieldCatalog)[K] {
return kbxFieldCatalog[key]
}
@@ -0,0 +1,175 @@
// generated from contracts/integrations/kbx.integrations.json; do not edit.
import type { KbxIntegrationDefinition } from '../integration'
export const kbxIntegrationSourceSha256 = '607853e43ab35fdacc1adea7d5b470dfad791f6718afba215b5600c974b0642e' as const
export const kbxIntegrationCatalog = {
"integration.oms.wms.dispatch": {
"id": "integration.oms.wms.dispatch",
"title": "OMS → WMS 출고지시 전달",
"ownerModule": "OMS",
"direction": "outbound",
"transport": "outbox-http",
"criticality": "high",
"sourceEvent": "OrderShipmentRequested",
"target": "WMS",
"delivery": "at-least-once",
"ordering": "per-aggregate",
"idempotency": "event-id",
"timeoutMs": 3000,
"shortRetry": {
"maxRetryAttempts": 2,
"baseDelayMs": 250,
"backoff": "exponential"
},
"longRetry": {
"scheduler": "hangfire",
"maxAttempts": 8,
"scheduleSeconds": [
10,
30,
120,
300,
900,
1800,
3600,
7200
]
},
"circuitBreaker": {
"failureRatio": 0.5,
"samplingSeconds": 30,
"minimumThroughput": 10,
"breakSeconds": 30
},
"retryable": [
"network",
"timeout",
"http-408",
"http-429",
"http-5xx",
"circuit-open"
],
"permanent": [
"contract-invalid",
"http-400",
"http-401",
"http-403",
"http-404",
"domain-rejected"
],
"terminalAction": "operations-exception",
"userVisible": true
},
"integration.wms.oms.picking-result": {
"id": "integration.wms.oms.picking-result",
"title": "WMS → OMS 피킹결과 전달",
"ownerModule": "WMS",
"direction": "outbound",
"transport": "outbox-http",
"criticality": "high",
"sourceEvent": "PickingCompleted",
"target": "OMS",
"delivery": "at-least-once",
"ordering": "per-aggregate",
"idempotency": "event-id",
"timeoutMs": 3000,
"shortRetry": {
"maxRetryAttempts": 2,
"baseDelayMs": 250,
"backoff": "exponential"
},
"longRetry": {
"scheduler": "hangfire",
"maxAttempts": 8,
"scheduleSeconds": [
10,
30,
120,
300,
900,
1800,
3600,
7200
]
},
"circuitBreaker": {
"failureRatio": 0.5,
"samplingSeconds": 30,
"minimumThroughput": 10,
"breakSeconds": 30
},
"retryable": [
"network",
"timeout",
"http-408",
"http-429",
"http-5xx",
"circuit-open"
],
"permanent": [
"contract-invalid",
"http-400",
"http-401",
"http-403",
"http-404",
"domain-rejected"
],
"terminalAction": "operations-exception",
"userVisible": true
},
"integration.carrier.tracking": {
"id": "integration.carrier.tracking",
"title": "배송사 송장/배송상태 연계",
"ownerModule": "OMS",
"direction": "outbound",
"transport": "outbox-http",
"criticality": "medium",
"sourceEvent": "TrackingSubmissionRequested",
"target": "CarrierGateway",
"delivery": "at-least-once",
"ordering": "none",
"idempotency": "business-key",
"timeoutMs": 5000,
"shortRetry": {
"maxRetryAttempts": 2,
"baseDelayMs": 500,
"backoff": "exponential"
},
"longRetry": {
"scheduler": "hangfire",
"maxAttempts": 6,
"scheduleSeconds": [
30,
120,
600,
1800,
3600,
7200
]
},
"circuitBreaker": {
"failureRatio": 0.5,
"samplingSeconds": 60,
"minimumThroughput": 10,
"breakSeconds": 60
},
"retryable": [
"network",
"timeout",
"http-408",
"http-429",
"http-5xx",
"circuit-open"
],
"permanent": [
"contract-invalid",
"http-400",
"http-401",
"http-403",
"http-404",
"domain-rejected"
],
"terminalAction": "operations-exception",
"userVisible": true
}
} as const satisfies Record<string,KbxIntegrationDefinition>
export type KbxIntegrationId = keyof typeof kbxIntegrationCatalog
@@ -0,0 +1,574 @@
// generated from contracts/authorization/kbx.authorization.json
export const kbxPermissionCatalog = [
{
"id": "common.ai.use",
"module": "COMMON",
"resource": "ai",
"action": "use",
"risk": "medium",
"description": "AI 설명·추천·초안 기능 사용",
"presentation": "disable",
"backendRequired": true
},
{
"id": "common.ai.execute",
"module": "COMMON",
"resource": "ai",
"action": "execute",
"risk": "high",
"description": "AI 승인 제안의 실제 Command 실행 허용",
"presentation": "disable",
"backendRequired": true
},
{
"id": "common.operations.claim",
"module": "COMMON",
"resource": "operations",
"action": "claim",
"risk": "medium",
"description": "업무 예외를 내 처리건으로 지정",
"presentation": "disable",
"backendRequired": true
},
{
"id": "common.operations.create",
"module": "COMMON",
"resource": "operations",
"action": "create",
"risk": "medium",
"description": "대사 결과에서 운영 예외 생성",
"presentation": "disable",
"backendRequired": true
},
{
"id": "common.operations.read",
"module": "COMMON",
"resource": "operations",
"action": "read",
"risk": "low",
"description": "업무 예외 센터 조회",
"presentation": "hide",
"backendRequired": true
},
{
"id": "common.operations.resolve",
"module": "COMMON",
"resource": "operations",
"action": "resolve",
"risk": "high",
"description": "수동 해결이 허용된 운영 예외 해결",
"presentation": "disable",
"backendRequired": true
},
{
"id": "common.operations.retry",
"module": "COMMON",
"resource": "operations",
"action": "retry",
"risk": "high",
"description": "등록된 안전한 예외 Action 재처리",
"presentation": "disable",
"backendRequired": true
},
{
"id": "common.reconcile.read",
"module": "COMMON",
"resource": "reconcile",
"action": "read",
"risk": "low",
"description": "업무 데이터 대사 조회",
"presentation": "hide",
"backendRequired": true
},
{
"id": "common.runtime.read",
"module": "COMMON",
"resource": "runtime",
"action": "read",
"risk": "low",
"description": "런타임 상태·작업·알림 조회",
"presentation": "hide",
"backendRequired": true
},
{
"id": "common.suggestion.create",
"module": "COMMON",
"resource": "suggestion",
"action": "create",
"risk": "low",
"description": "현재 화면 사용자 제안 등록",
"presentation": "disable",
"backendRequired": true
},
{
"id": "erp.inventory.move.confirm",
"module": "ERP",
"resource": "inventory-move",
"action": "confirm",
"risk": "high",
"description": "재고이동 확정",
"presentation": "disable",
"backendRequired": true
},
{
"id": "erp.inventory.move.read",
"module": "ERP",
"resource": "inventory-move",
"action": "read",
"risk": "low",
"description": "재고이동 조회",
"presentation": "hide",
"backendRequired": true
},
{
"id": "erp.inventory.move.receive",
"module": "ERP",
"resource": "inventory-move",
"action": "receive",
"risk": "high",
"description": "이동재고 입고완료",
"presentation": "disable",
"backendRequired": true
},
{
"id": "erp.inventory.move.ship",
"module": "ERP",
"resource": "inventory-move",
"action": "ship",
"risk": "high",
"description": "이동재고 출고",
"presentation": "disable",
"backendRequired": true
},
{
"id": "erp.inventory.move.write",
"module": "ERP",
"resource": "inventory-move",
"action": "write",
"risk": "medium",
"description": "재고이동 작성·수정",
"presentation": "disable",
"backendRequired": true
},
{
"id": "erp.inventory.read",
"module": "ERP",
"resource": "inventory",
"action": "read",
"risk": "low",
"description": "재고현황 조회",
"presentation": "hide",
"backendRequired": true
},
{
"id": "erp.item.price.read",
"module": "ERP",
"resource": "item-price",
"action": "read",
"risk": "low",
"description": "품목 단가 일괄등록 화면 조회",
"presentation": "hide",
"backendRequired": true
},
{
"id": "erp.item.price.write",
"module": "ERP",
"resource": "item-price",
"action": "write",
"risk": "medium",
"description": "품목 단가 일괄등록·수정",
"presentation": "disable",
"backendRequired": true
},
{
"id": "erp.item.create",
"module": "ERP",
"resource": "item",
"action": "create",
"risk": "medium",
"description": "품목 신규·복사",
"presentation": "disable",
"backendRequired": true
},
{
"id": "erp.item.read",
"module": "ERP",
"resource": "item",
"action": "read",
"risk": "low",
"description": "품목 조회",
"presentation": "hide",
"backendRequired": true
},
{
"id": "erp.item.write",
"module": "ERP",
"resource": "item",
"action": "write",
"risk": "medium",
"description": "품목 수정·사용중지",
"presentation": "disable",
"backendRequired": true
},
{
"id": "erp.purchase.cancel",
"module": "ERP",
"resource": "purchase",
"action": "cancel",
"risk": "high",
"description": "구매 취소",
"presentation": "disable",
"backendRequired": true
},
{
"id": "erp.purchase.confirm",
"module": "ERP",
"resource": "purchase",
"action": "confirm",
"risk": "high",
"description": "구매 확정",
"presentation": "disable",
"backendRequired": true
},
{
"id": "erp.purchase.read",
"module": "ERP",
"resource": "purchase",
"action": "read",
"risk": "low",
"description": "구매 조회",
"presentation": "hide",
"backendRequired": true
},
{
"id": "erp.purchase.write",
"module": "ERP",
"resource": "purchase",
"action": "write",
"risk": "medium",
"description": "구매 작성·수정",
"presentation": "disable",
"backendRequired": true
},
{
"id": "imports.execute",
"module": "COMMON",
"resource": "imports",
"action": "execute",
"risk": "high",
"description": "Excel Import 생성·검증·반영",
"presentation": "disable",
"backendRequired": true
},
{
"id": "kbx.design.read",
"module": "COMMON",
"resource": "design-system",
"action": "read",
"risk": "low",
"description": "KBX 내부 컴포넌트 카탈로그 조회",
"presentation": "hide",
"backendRequired": true
},
{
"id": "oms.claim.approve",
"module": "OMS",
"resource": "claim",
"action": "approve",
"risk": "high",
"description": "클레임 승인",
"presentation": "disable",
"backendRequired": true
},
{
"id": "oms.claim.hold",
"module": "OMS",
"resource": "claim",
"action": "hold",
"risk": "medium",
"description": "클레임 보류",
"presentation": "disable",
"backendRequired": true
},
{
"id": "oms.claim.process",
"module": "OMS",
"resource": "claim",
"action": "process",
"risk": "high",
"description": "클레임 처리 시작·완료",
"presentation": "disable",
"backendRequired": true
},
{
"id": "oms.claim.read",
"module": "OMS",
"resource": "claim",
"action": "read",
"risk": "low",
"description": "반품·클레임 조회",
"presentation": "hide",
"backendRequired": true
},
{
"id": "oms.order.create",
"module": "OMS",
"resource": "order",
"action": "create",
"risk": "medium",
"description": "주문 신규 등록",
"presentation": "disable",
"backendRequired": true
},
{
"id": "oms.order.import",
"module": "OMS",
"resource": "order",
"action": "import",
"risk": "high",
"description": "주문 Excel Import 실행",
"presentation": "disable",
"backendRequired": true
},
{
"id": "oms.order.read",
"module": "OMS",
"resource": "order",
"action": "read",
"risk": "low",
"description": "주문 조회",
"presentation": "hide",
"backendRequired": true
},
{
"id": "oms.order.ship",
"module": "OMS",
"resource": "order",
"action": "ship",
"risk": "high",
"description": "주문 출고지시",
"presentation": "disable",
"backendRequired": true
},
{
"id": "oms.order.recipient.unmask",
"module": "OMS",
"resource": "order-recipient",
"action": "sensitive-read",
"risk": "high",
"description": "주문 수취인 개인정보 전체보기",
"presentation": "disable",
"backendRequired": true
},
{
"id": "oms.order.recipient.export",
"module": "OMS",
"resource": "order-recipient",
"action": "sensitive-export",
"risk": "high",
"description": "주문 수취인 개인정보 비마스킹 Export",
"presentation": "disable",
"backendRequired": true
},
{
"id": "wms.inventory.count",
"module": "WMS",
"resource": "inventory",
"action": "count",
"risk": "high",
"description": "현장 재고실사 실행",
"presentation": "hide",
"backendRequired": true
},
{
"id": "wms.picking.execute",
"module": "WMS",
"resource": "picking",
"action": "execute",
"risk": "high",
"description": "출고 피킹 작업 실행",
"presentation": "hide",
"backendRequired": true
},
{
"id": "wms.putaway.execute",
"module": "WMS",
"resource": "putaway",
"action": "execute",
"risk": "high",
"description": "입고 적치 작업 실행",
"presentation": "hide",
"backendRequired": true
},
{
"id": "wms.receiving.execute",
"module": "WMS",
"resource": "receiving",
"action": "execute",
"risk": "high",
"description": "입고 검수 작업 실행",
"presentation": "hide",
"backendRequired": true
},
{
"id": "wms.work.execute",
"module": "WMS",
"resource": "work",
"action": "execute",
"risk": "high",
"description": "WMS 작업 시작",
"presentation": "disable",
"backendRequired": true
},
{
"id": "wms.work.read",
"module": "WMS",
"resource": "work",
"action": "read",
"risk": "low",
"description": "WMS 작업 Queue 조회",
"presentation": "hide",
"backendRequired": true
},
{
"id": "common.telemetry.write",
"module": "COMMON",
"resource": "telemetry",
"action": "write",
"risk": "low",
"description": "민감정보를 포함하지 않는 KBX 의미 단위 UX Telemetry 전송",
"presentation": "hide",
"backendRequired": true
},
{
"id": "common.ux.read",
"module": "COMMON",
"resource": "ux-metrics",
"action": "read",
"risk": "medium",
"description": "KBX UX 품질·자동화 지표 조회",
"presentation": "hide",
"backendRequired": true
},
{
"id": "common.experiment.evaluate",
"module": "COMMON",
"resource": "experiment",
"action": "evaluate",
"risk": "low",
"description": "현재 사용자에 대한 안전한 UX 실험 Variant 평가",
"presentation": "hide",
"backendRequired": true
},
{
"id": "common.experiment.read",
"module": "COMMON",
"resource": "experiment",
"action": "read",
"risk": "low",
"description": "KBX UX 실험·점진배포 상태 조회",
"presentation": "hide",
"backendRequired": true
},
{
"id": "common.experiment.manage",
"module": "COMMON",
"resource": "experiment",
"action": "manage",
"risk": "high",
"description": "UX 실험 점진배포 비율 변경·일시중지·즉시 롤백",
"presentation": "disable",
"backendRequired": true
},
{
"id": "common.integration.read",
"module": "COMMON",
"resource": "integration",
"action": "read",
"risk": "low",
"description": "업무 외부연계 상태·시도 이력 조회",
"presentation": "hide",
"backendRequired": true
},
{
"id": "common.integration.retry",
"module": "COMMON",
"resource": "integration",
"action": "retry",
"risk": "high",
"description": "최종 실패한 idempotent 외부연계의 수동 재처리 예약",
"presentation": "disable",
"backendRequired": true
},
{
"id": "common.external-data.read",
"module": "COMMON",
"resource": "external-data",
"action": "read",
"risk": "low",
"description": "외부 데이터셋의 출처·신선도·캐시 상태 조회",
"presentation": "hide",
"backendRequired": true
},
{
"id": "common.external-data.refresh",
"module": "COMMON",
"resource": "external-data",
"action": "refresh",
"risk": "medium",
"description": "승인된 외부 데이터셋의 명시적 재수집 요청",
"presentation": "disable",
"backendRequired": true
},
{
"id": "oms.order.confirm",
"module": "OMS",
"resource": "order",
"action": "confirm",
"risk": "high",
"description": "작성 주문 확정",
"presentation": "disable",
"backendRequired": true
},
{
"id": "oms.order.write",
"module": "OMS",
"resource": "order",
"action": "write",
"risk": "medium",
"description": "작성 상태 주문 수정",
"presentation": "disable",
"backendRequired": true
}
] as const
export const kbxSensitiveDataPolicies = [
{
"id": "oms.order.recipient",
"fields": [
"receiverName",
"phone",
"postalCode",
"address1",
"address2"
],
"defaultExposure": "masked",
"viewPermission": "oms.order.read",
"revealPermission": "oms.order.recipient.unmask",
"unmaskedExportPermission": "oms.order.recipient.export",
"auditRequired": true,
"aiExposure": "masked-only",
"telemetryExposure": "never",
"reasonRequired": false
}
] as const
export const kbxAiAuthorizationPolicy = {
"usePermission": "common.ai.use",
"executePermission": "common.ai.execute",
"executeRequiresProposalPermission": true,
"defaultCapabilities": [
"explain",
"suggest",
"draft"
]
} as const
export type KbxPermissionId = typeof kbxPermissionCatalog[number]['id']
@@ -0,0 +1,215 @@
// generated from contracts/providers/kbx.providers.json; do not edit.
import type { KbxExternalProviderDefinition } from '../provider'
export const kbxProviderSourceSha256='5515e5e002767ed895ea0442003829b0bad8fa9c7a0693ce6cdcbd65969b830f' as const
export const kbxExternalProviderCatalog={
"provider.krx.openapi": {
"id": "provider.krx.openapi",
"title": "KRX Data Marketplace OPEN API",
"ownerModule": "COMMON",
"purpose": "market-data-readonly",
"mutationAllowed": false,
"officialSources": [
"https://openapi.krx.co.kr/contents/OPP/INFO/OPPINFO003.jsp",
"https://openapi.krx.co.kr/contents/OPP/INFO/service/OPPINFO004.cmd"
],
"official": {
"approvalRequired": true,
"authentication": {
"type": "header-api-key",
"headerName": "AUTH_KEY"
},
"serviceUrlPolicy": "service-specific-from-approved-KRX-definition",
"dataAvailabilityNote": "서비스 목록은 다수 데이터에 대해 2010년 이후 제공범위를 안내함",
"numericRateLimit": null
},
"kbxPolicy": {
"allowedHostSuffix": ".krx.co.kr",
"credentialKey": "ExternalProviders:Krx:AuthKey",
"maxConcurrency": 2,
"timeoutMs": 5000,
"retryHttpStatuses": [
408,
429,
500,
502,
503,
504
],
"maxRetryAttempts": 1,
"serviceDefinitionRequired": true,
"cacheable": true
},
"operations": [
{
"id": "krx.approved-service.invoke",
"method": "provider-defined-read",
"path": "provider-defined",
"response": "json-or-xml",
"notes": "실제 URL/메서드는 KRX에서 승인된 개별 서비스 명세를 구성값으로 등록. 저장소에서 추정하지 않음."
}
]
},
"provider.opendart": {
"id": "provider.opendart",
"title": "금융감독원 OPENDART OpenAPI",
"ownerModule": "COMMON",
"purpose": "disclosure-data-readonly",
"mutationAllowed": false,
"officialSources": [
"https://opendart.fss.or.kr/guide/detail.do?apiGrpCd=DS001&apiId=2019001",
"https://opendart.fss.or.kr/guide/detail.do?apiGrpCd=DS001&apiId=2019002",
"https://opendart.fss.or.kr/guide/detail.do?apiGrpCd=DS001&apiId=2019018"
],
"official": {
"baseUri": "https://opendart.fss.or.kr/api/",
"authentication": {
"type": "query-api-key",
"parameterName": "crtfc_key",
"length": 40
},
"statusField": "status",
"messageField": "message",
"statusCodes": {
"100": "invalid-field",
"101": "invalid-access",
"800": "maintenance",
"900": "undefined-error",
"901": "expired-personal-data-retention",
"000": "success",
"010": "unregistered-key",
"011": "disabled-key",
"012": "forbidden-ip",
"013": "no-data",
"014": "file-missing",
"020": "request-limit-exceeded",
"021": "company-count-exceeded"
},
"rateLimitNote": "020은 일반적으로 20,000건 이상의 요청에서 발생할 수 있으나 서비스별 제한이 다를 수 있음"
},
"kbxPolicy": {
"credentialKey": "ExternalProviders:OpenDart:ApiKey",
"timeoutMs": 5000,
"maxConcurrency": 2,
"retryStatusCodes": [
"020",
"800",
"900"
],
"maxRetryAttempts": 1,
"noDataStatusCodes": [
"013"
],
"cacheable": true
},
"operations": [
{
"id": "opendart.disclosures",
"method": "GET",
"path": "list.json",
"response": "json"
},
{
"id": "opendart.company",
"method": "GET",
"path": "company.json",
"response": "json"
},
{
"id": "opendart.corp-code",
"method": "GET",
"path": "corpCode.xml",
"response": "zip-binary"
}
]
},
"provider.kis.market-data": {
"id": "provider.kis.market-data",
"title": "한국투자증권 KIS Open API — 국내주식 시세",
"ownerModule": "COMMON",
"purpose": "market-data-readonly",
"mutationAllowed": false,
"officialSources": [
"https://apiportal.koreainvestment.com/apiservice-apiservice",
"https://apiportal.koreainvestment.com/community/10000000-0000-0011-0000-000000000001/post/d0d1a83f-6f8d-4437-9700-6d26702fd989",
"https://github.com/koreainvestment/open-trading-api/blob/main/examples_llm/domestic_stock/inquire_price/inquire_price.py",
"https://github.com/koreainvestment/open-trading-api/blob/main/examples_llm/kis_auth.py"
],
"official": {
"productionBaseUri": "https://openapi.koreainvestment.com:9443",
"sandboxBaseUri": "https://openapivts.koreainvestment.com:29443",
"authentication": {
"type": "oauth2-client-credentials",
"tokenPath": "/oauth2/tokenP",
"bodyFields": [
"grant_type",
"appkey",
"appsecret"
],
"grantType": "client_credentials"
},
"tokenValidityHours": 24,
"tokenRenewalCycleHours": 6,
"headers": [
"authorization",
"appkey",
"appsecret",
"tr_id",
"custtype",
"tr_cont"
],
"responseSuccess": {
"field": "rt_cd",
"value": "0"
},
"rateLimitsAsOf": "2026-04-20",
"productionRequestsPerSecond": 18,
"sandboxRequestsPerSecond": 1,
"tokenRequestsPerSecond": 1,
"concurrentSpacingRecommendationMs": [
100,
150
]
},
"kbxPolicy": {
"appKeyCredential": "ExternalProviders:Kis:AppKey",
"appSecretCredential": "ExternalProviders:Kis:AppSecret",
"environmentKey": "ExternalProviders:Kis:Environment",
"readOnlyOnly": true,
"tokenRefreshSkewMinutes": 5,
"productionMinimumSpacingMs": 100,
"sandboxMinimumSpacingMs": 1000,
"timeoutMs": 5000,
"maxRetryAttempts": 1,
"retryHttpStatuses": [
408,
429,
500,
502,
503,
504
],
"cacheable": true
},
"operations": [
{
"id": "kis.oauth.token",
"method": "POST",
"path": "/oauth2/tokenP",
"response": "json",
"authOperation": true
},
{
"id": "kis.domestic-stock.current-price",
"method": "GET",
"path": "/uapi/domestic-stock/v1/quotations/inquire-price",
"trId": "FHKST01010100",
"response": "json",
"query": [
"FID_COND_MRKT_DIV_CODE",
"FID_INPUT_ISCD"
]
}
]
}
} as const satisfies Record<string,KbxExternalProviderDefinition>
export type KbxExternalProviderId=keyof typeof kbxExternalProviderCatalog
@@ -0,0 +1,956 @@
// generated from contracts/screens/kbx.screen-recipes.json; do not edit.
// SHA256: 97ab56bc54149f32ea16577369c4ec29579b516ea464cb42010b31e4dc413e60
import type { KbxScreenRecipeDefinition } from '../screen'
export const kbxScreenRecipeContractVersion="1.1.0" as const
export const kbxScreenRecipeCatalog={
"T01": {
"code": "T01",
"type": "list",
"templateComponent": "KbxListPage",
"defaultCommands": [
{
"id": "search",
"label": "조회",
"group": "query",
"shortcut": "F3",
"permissionKind": "none"
},
{
"id": "excel",
"label": "엑셀",
"group": "output",
"permissionKind": "none"
}
],
"requiredPolicies": [
"server-read-model",
"tanstack-query",
"search-condition-preservation",
"server-side-bulk-selection"
],
"recoveryPolicies": [
"idle-before-first-search",
"retain-grid-during-refresh",
"retry-with-search-context",
"partial-bulk-result"
],
"securityPolicies": [
"screen-permission",
"command-permission",
"safe-drilldown-route",
"masked-sensitive-cells"
],
"canonicalScenarioIds": [
"scenario.oms.order-list.bulk-ship",
"scenario.oms.order-list.all-filtered-ship"
],
"scaffoldSurfaces": [
"search",
"content",
"summary"
],
"testProfile": {
"requiredScenarioKinds": [
"e2e"
],
"requiredTags": [
"bulk",
"idempotency"
],
"requiredEvidence": [
"playwright-trace"
],
"requiredChecks": [
"idle-search",
"refresh-retains-context",
"bulk-partial-result",
"safe-drilldown"
]
}
},
"T02": {
"code": "T02",
"type": "master",
"templateComponent": "KbxMasterPage",
"defaultCommands": [
{
"id": "search",
"label": "조회",
"group": "query",
"shortcut": "F3",
"permissionKind": "none"
},
{
"id": "new",
"label": "신규",
"group": "edit",
"permissionKind": "write"
},
{
"id": "save",
"label": "저장",
"group": "edit",
"shortcut": "F8",
"variant": "primary",
"permissionKind": "write"
},
{
"id": "excel",
"label": "엑셀",
"group": "output",
"permissionKind": "none"
}
],
"requiredPolicies": [
"normalized-write-model",
"lookup-provider",
"server-validation",
"optimistic-concurrency",
"audit"
],
"recoveryPolicies": [
"dirty-navigation-guard",
"conflict-reload",
"field-validation-focus",
"readonly-after-terminal-state"
],
"securityPolicies": [
"screen-permission",
"write-command-permission",
"server-enforcement",
"sensitive-value-masking"
],
"canonicalScenarioIds": [
"scenario.erp.item-master.lifecycle",
"scenario.erp.item-master.keyboard-recovery"
],
"scaffoldSurfaces": [
"list",
"detail"
],
"testProfile": {
"requiredScenarioKinds": [
"e2e",
"integration"
],
"requiredTags": [
"master-crud",
"concurrency",
"audit"
],
"requiredEvidence": [
"playwright-trace",
"db-assertions"
],
"requiredChecks": [
"permission-aware-save",
"dirty-guard",
"conflict-reload",
"audit-visible"
]
}
},
"T03": {
"code": "T03",
"type": "transaction",
"templateComponent": "KbxTransactionPage",
"defaultCommands": [
{
"id": "new",
"label": "신규",
"group": "edit",
"permissionKind": "write"
},
{
"id": "save",
"label": "저장",
"group": "edit",
"shortcut": "F8",
"variant": "primary",
"permissionKind": "write"
},
{
"id": "excel",
"label": "엑셀",
"group": "output",
"permissionKind": "none"
}
],
"requiredPolicies": [
"normalized-write-model",
"header-detail-transaction",
"server-validation",
"optimistic-concurrency",
"audit",
"outbox"
],
"recoveryPolicies": [
"dirty-navigation-guard",
"conflict-reload",
"row-key-validation",
"double-submit-guard"
],
"securityPolicies": [
"screen-permission",
"write-command-permission",
"workflow-permission",
"server-enforcement"
],
"canonicalScenarioIds": [
"scenario.oms.order-register.keyboard",
"scenario.oms.order-register.conflict",
"scenario.oms.order-register.lifecycle"
],
"scaffoldSurfaces": [
"header",
"detail",
"summary"
],
"testProfile": {
"requiredScenarioKinds": [
"e2e",
"integration"
],
"requiredTags": [
"keyboard",
"lookup",
"conflict",
"workflow"
],
"requiredEvidence": [
"playwright-trace",
"problem-json"
],
"requiredChecks": [
"f2-lookup",
"f8-save",
"row-key-validation",
"conflict-recovery"
]
}
},
"T04": {
"code": "T04",
"type": "fast-entry",
"templateComponent": "KbxFastEntryPage",
"defaultCommands": [
{
"id": "new",
"label": "신규",
"group": "edit",
"permissionKind": "write"
},
{
"id": "save",
"label": "저장",
"group": "edit",
"shortcut": "F8",
"variant": "primary",
"permissionKind": "write"
},
{
"id": "excel",
"label": "엑셀",
"group": "output",
"permissionKind": "none"
}
],
"requiredPolicies": [
"typed-clipboard-normalization",
"stable-client-row-key",
"bulk-server-validation",
"idempotent-bulk-save"
],
"recoveryPolicies": [
"error-cell-navigation",
"paste-error-summary",
"dirty-navigation-guard",
"partial-save-result"
],
"securityPolicies": [
"screen-permission",
"write-command-permission",
"lookup-entity-resolution",
"server-enforcement"
],
"canonicalScenarioIds": [
"scenario.erp.item-price.fast-entry",
"scenario.oms.order-register.fast-entry"
],
"scaffoldSurfaces": [
"guide",
"content",
"validation",
"summary"
],
"testProfile": {
"requiredScenarioKinds": [
"e2e"
],
"requiredTags": [
"fast-entry",
"paste",
"fill-down",
"idempotency"
],
"requiredEvidence": [
"playwright-trace",
"problem-json"
],
"requiredChecks": [
"multi-cell-paste",
"fill-down",
"error-navigation",
"partial-save-result"
]
}
},
"T05": {
"code": "T05",
"type": "master-detail",
"templateComponent": "KbxMasterDetailPage",
"defaultCommands": [
{
"id": "search",
"label": "조회",
"group": "query",
"shortcut": "F3",
"permissionKind": "none"
},
{
"id": "excel",
"label": "엑셀",
"group": "output",
"permissionKind": "none"
}
],
"requiredPolicies": [
"server-read-model",
"master-selection-context",
"detail-projection",
"n-plus-one-prohibition"
],
"recoveryPolicies": [
"selection-restore-after-refresh",
"retain-master-on-detail-error",
"safe-drilldown",
"empty-master-detail-reset"
],
"securityPolicies": [
"screen-permission",
"safe-drilldown-route",
"masked-sensitive-detail"
],
"canonicalScenarioIds": [
"scenario.erp.inventory.master-detail-context"
],
"scaffoldSurfaces": [
"search",
"master",
"detail"
],
"testProfile": {
"requiredScenarioKinds": [
"e2e"
],
"requiredTags": [
"master-detail",
"context-retention",
"drill-down"
],
"requiredEvidence": [
"playwright-trace",
"api-transcript"
],
"requiredChecks": [
"master-selection-context",
"detail-error-retains-master",
"history-context",
"safe-drilldown"
]
}
},
"T06": {
"code": "T06",
"type": "queue",
"templateComponent": "KbxQueuePage",
"defaultCommands": [
{
"id": "search",
"label": "조회",
"group": "query",
"shortcut": "F3",
"permissionKind": "none"
}
],
"requiredPolicies": [
"exception-first-projection",
"sla-state",
"server-side-bulk-selection",
"audit"
],
"recoveryPolicies": [
"partial-action-result",
"retryable-vs-terminal-error",
"stale-event-suppression",
"detail-context-retention"
],
"securityPolicies": [
"screen-permission",
"exception-action-permission",
"server-enforcement"
],
"canonicalScenarioIds": [
"scenario.common.operations.stale-event",
"scenario.integration.manual-retry.idempotent",
"scenario.common.operations.queue-recovery"
],
"scaffoldSurfaces": [
"summary",
"exceptions",
"content"
],
"testProfile": {
"requiredScenarioKinds": [
"e2e",
"integration"
],
"requiredTags": [
"exception",
"recovery",
"context-retention"
],
"requiredEvidence": [
"playwright-trace",
"db-assertions"
],
"requiredChecks": [
"exception-first",
"detail-context-retention",
"retryable-vs-terminal",
"partial-action-result"
]
}
},
"T07": {
"code": "T07",
"type": "reconcile",
"templateComponent": "KbxReconcilePage",
"defaultCommands": [
{
"id": "search",
"label": "조회",
"group": "query",
"shortcut": "F3",
"permissionKind": "none"
}
],
"requiredPolicies": [
"expected-actual-difference-projection",
"reason-code",
"resolution-state",
"audit"
],
"recoveryPolicies": [
"grace-period-before-mismatch",
"mismatch-only-filter",
"resolution-retry",
"retain-comparison-context"
],
"securityPolicies": [
"screen-permission",
"resolution-permission",
"server-enforcement"
],
"canonicalScenarioIds": [
"scenario.common.reconcile.grace-period",
"scenario.common.reconcile.mismatch-recovery"
],
"scaffoldSurfaces": [
"search",
"content",
"resolution"
],
"testProfile": {
"requiredScenarioKinds": [
"e2e",
"integration"
],
"requiredTags": [
"reconcile",
"mismatch-filter",
"eventual-consistency"
],
"requiredEvidence": [
"playwright-trace",
"db-assertions"
],
"requiredChecks": [
"mismatch-only-filter",
"grace-period",
"resolution-retry",
"comparison-context"
]
}
},
"T08": {
"code": "T08",
"type": "import",
"templateComponent": "KbxImportPage",
"defaultCommands": [],
"requiredPolicies": [
"staging-before-domain-write",
"mapping-source-traceability",
"background-job-threshold",
"idempotent-commit",
"audit"
],
"recoveryPolicies": [
"session-mapping-isolation",
"validation-error-download",
"partial-commit-result",
"job-refresh-recovery"
],
"securityPolicies": [
"screen-permission",
"file-preflight",
"commit-permission",
"server-validation",
"sensitive-column-policy"
],
"canonicalScenarioIds": [
"scenario.oms.order-import.partial"
],
"scaffoldSurfaces": [
"steps",
"content",
"result"
],
"testProfile": {
"requiredScenarioKinds": [
"e2e"
],
"requiredTags": [
"excel",
"mapping",
"staging",
"partial-success",
"job"
],
"requiredEvidence": [
"playwright-trace",
"problem-json"
],
"requiredChecks": [
"mapping-isolation",
"validation-download",
"commit-confirm",
"job-refresh-recovery"
]
}
},
"T09": {
"code": "T09",
"type": "wms-mobile",
"templateComponent": "KbxWmsMobilePage",
"defaultCommands": [],
"requiredPolicies": [
"barcode-normalization",
"server-validation-before-success",
"idempotent-scan-command",
"network-state",
"offline-command-policy"
],
"recoveryPolicies": [
"duplicate-scan-debounce",
"response-loss-replay",
"wrong-item-no-retry",
"pending-command-visibility"
],
"securityPolicies": [
"screen-permission",
"execute-permission",
"server-enforcement",
"offline-command-allowlist"
],
"canonicalScenarioIds": [
"scenario.wms.picking.idempotent-replay",
"scenario.wms.picking.wrong-item"
],
"scaffoldSurfaces": [
"context",
"content",
"actions"
],
"testProfile": {
"requiredScenarioKinds": [
"e2e"
],
"requiredTags": [
"scanner",
"idempotency",
"network",
"retry-policy"
],
"requiredEvidence": [
"playwright-trace",
"api-transcript"
],
"requiredChecks": [
"scan-normalization",
"server-before-success",
"response-loss-replay",
"wrong-item-no-retry"
]
}
}
} as const satisfies Record<string,KbxScreenRecipeDefinition>
export const kbxScreenRecipeVerificationCatalog={
"T01": {
"code": "T01",
"type": "list",
"canonicalScenarioIds": [
"scenario.oms.order-list.bulk-ship",
"scenario.oms.order-list.all-filtered-ship"
],
"scenarioKinds": [
"contract",
"e2e"
],
"tags": [
"all-filtered",
"audit",
"bulk",
"filter-snapshot",
"golden-screen",
"idempotency",
"outbox"
],
"evidence": [
"api-transcript",
"correlation-ids",
"db-assertions",
"playwright-trace",
"screenshot",
"vitest-contract"
],
"requiredChecks": [
"idle-search",
"refresh-retains-context",
"bulk-partial-result",
"safe-drilldown"
],
"missingScenarioKinds": [],
"missingTags": [],
"missingEvidence": [],
"complete": true
},
"T02": {
"code": "T02",
"type": "master",
"canonicalScenarioIds": [
"scenario.erp.item-master.lifecycle",
"scenario.erp.item-master.keyboard-recovery"
],
"scenarioKinds": [
"e2e",
"integration"
],
"tags": [
"audit",
"concurrency",
"golden-screen",
"keyboard",
"master-crud",
"permission",
"recovery",
"state-policy"
],
"evidence": [
"api-transcript",
"db-assertions",
"playwright-trace",
"problem-json",
"screenshot"
],
"requiredChecks": [
"permission-aware-save",
"dirty-guard",
"conflict-reload",
"audit-visible"
],
"missingScenarioKinds": [],
"missingTags": [],
"missingEvidence": [],
"complete": true
},
"T03": {
"code": "T03",
"type": "transaction",
"canonicalScenarioIds": [
"scenario.oms.order-register.keyboard",
"scenario.oms.order-register.conflict",
"scenario.oms.order-register.lifecycle"
],
"scenarioKinds": [
"e2e",
"integration"
],
"tags": [
"audit",
"concurrency",
"conflict",
"golden-screen",
"keyboard",
"lookup",
"recovery",
"state-policy",
"transaction",
"validation",
"workflow"
],
"evidence": [
"api-transcript",
"db-assertions",
"playwright-trace",
"problem-json",
"screenshot"
],
"requiredChecks": [
"f2-lookup",
"f8-save",
"row-key-validation",
"conflict-recovery"
],
"missingScenarioKinds": [],
"missingTags": [],
"missingEvidence": [],
"complete": true
},
"T04": {
"code": "T04",
"type": "fast-entry",
"canonicalScenarioIds": [
"scenario.erp.item-price.fast-entry",
"scenario.oms.order-register.fast-entry"
],
"scenarioKinds": [
"contract",
"e2e"
],
"tags": [
"audit",
"fast-entry",
"fill-down",
"golden-screen",
"idempotency",
"inline-validation",
"lookup",
"outbox",
"paste"
],
"evidence": [
"api-transcript",
"component-catalog",
"db-assertions",
"playwright-trace",
"problem-json",
"vitest-contract"
],
"requiredChecks": [
"multi-cell-paste",
"fill-down",
"error-navigation",
"partial-save-result"
],
"missingScenarioKinds": [],
"missingTags": [],
"missingEvidence": [],
"complete": true
},
"T05": {
"code": "T05",
"type": "master-detail",
"canonicalScenarioIds": [
"scenario.erp.inventory.master-detail-context"
],
"scenarioKinds": [
"e2e"
],
"tags": [
"context-retention",
"drill-down",
"golden-screen",
"history",
"master-detail"
],
"evidence": [
"api-transcript",
"playwright-trace",
"screenshot"
],
"requiredChecks": [
"master-selection-context",
"detail-error-retains-master",
"history-context",
"safe-drilldown"
],
"missingScenarioKinds": [],
"missingTags": [],
"missingEvidence": [],
"complete": true
},
"T06": {
"code": "T06",
"type": "queue",
"canonicalScenarioIds": [
"scenario.common.operations.stale-event",
"scenario.integration.manual-retry.idempotent",
"scenario.common.operations.queue-recovery"
],
"scenarioKinds": [
"e2e",
"integration"
],
"tags": [
"context-retention",
"exception",
"golden-screen",
"idempotency",
"integration",
"manual-retry",
"ordering",
"outbox",
"permission",
"projection",
"queue",
"recovery"
],
"evidence": [
"api-transcript",
"correlation-ids",
"db-assertions",
"playwright-trace",
"projection-event-log",
"screenshot"
],
"requiredChecks": [
"exception-first",
"detail-context-retention",
"retryable-vs-terminal",
"partial-action-result"
],
"missingScenarioKinds": [],
"missingTags": [],
"missingEvidence": [],
"complete": true
},
"T07": {
"code": "T07",
"type": "reconcile",
"canonicalScenarioIds": [
"scenario.common.reconcile.grace-period",
"scenario.common.reconcile.mismatch-recovery"
],
"scenarioKinds": [
"e2e",
"integration"
],
"tags": [
"context-retention",
"eventual-consistency",
"golden-screen",
"mismatch-filter",
"reconcile",
"recovery"
],
"evidence": [
"api-transcript",
"clock-snapshot",
"db-assertions",
"playwright-trace",
"screenshot"
],
"requiredChecks": [
"mismatch-only-filter",
"grace-period",
"resolution-retry",
"comparison-context"
],
"missingScenarioKinds": [],
"missingTags": [],
"missingEvidence": [],
"complete": true
},
"T08": {
"code": "T08",
"type": "import",
"canonicalScenarioIds": [
"scenario.oms.order-import.partial"
],
"scenarioKinds": [
"e2e"
],
"tags": [
"excel",
"job",
"mapping",
"partial-success",
"staging"
],
"evidence": [
"api-transcript",
"db-assertions",
"error-workbook-metadata",
"import-session-snapshot",
"playwright-trace",
"problem-json"
],
"requiredChecks": [
"mapping-isolation",
"validation-download",
"commit-confirm",
"job-refresh-recovery"
],
"missingScenarioKinds": [],
"missingTags": [],
"missingEvidence": [],
"complete": true
},
"T09": {
"code": "T09",
"type": "wms-mobile",
"canonicalScenarioIds": [
"scenario.wms.picking.idempotent-replay",
"scenario.wms.picking.wrong-item"
],
"scenarioKinds": [
"e2e"
],
"tags": [
"business-error",
"golden-screen",
"idempotency",
"network",
"retry-policy",
"scanner"
],
"evidence": [
"api-transcript",
"db-assertions",
"playwright-trace",
"scanner-event-log"
],
"requiredChecks": [
"scan-normalization",
"server-before-success",
"response-loss-replay",
"wrong-item-no-retry"
],
"missingScenarioKinds": [],
"missingTags": [],
"missingEvidence": [],
"complete": true
}
} as const
@@ -0,0 +1,284 @@
// generated from contracts/telemetry/kbx.telemetry.json; do not edit.
export const kbxTelemetryCatalog = {
"screen.open": {
"name": "screen.open",
"category": "navigation",
"description": "화면 진입",
"allowedAttributes": [
"module",
"launchMode"
]
},
"screen.close": {
"name": "screen.close",
"category": "navigation",
"description": "화면 종료",
"allowedAttributes": [
"module",
"closeReason"
]
},
"task.start": {
"name": "task.start",
"category": "task",
"description": "측정 가능한 업무 Task 시작",
"allowedAttributes": [
"taskType"
]
},
"task.complete": {
"name": "task.complete",
"category": "task",
"description": "업무 Task 정상 완료",
"allowedAttributes": [
"taskType",
"result"
],
"requiresDuration": true
},
"task.abandon": {
"name": "task.abandon",
"category": "task",
"description": "업무 Task 중단",
"allowedAttributes": [
"taskType",
"reasonCode"
],
"requiresDuration": true
},
"interaction.execute": {
"name": "interaction.execute",
"category": "interaction",
"description": "KBX 의미 단위 업무 조작 실행",
"allowedAttributes": [
"interactionType",
"commandId"
]
},
"search.execute": {
"name": "search.execute",
"category": "interaction",
"description": "조회 실행",
"allowedAttributes": [
"resultBucket"
]
},
"command.execute": {
"name": "command.execute",
"category": "command",
"description": "업무 Command 실행 시도",
"allowedAttributes": [
"commandId",
"operationKind"
]
},
"command.succeeded": {
"name": "command.succeeded",
"category": "command",
"description": "업무 Command 성공",
"allowedAttributes": [
"commandId",
"operationKind"
],
"requiresDuration": true
},
"command.failed": {
"name": "command.failed",
"category": "command",
"description": "업무 Command 실패",
"allowedAttributes": [
"commandId",
"problemType",
"reasonCode"
],
"requiresDuration": true
},
"lookup.open": {
"name": "lookup.open",
"category": "lookup",
"description": "Lookup 열기",
"allowedAttributes": [
"entityType"
]
},
"lookup.select": {
"name": "lookup.select",
"category": "lookup",
"description": "Lookup 선택 완료",
"allowedAttributes": [
"entityType",
"selectionSource"
]
},
"grid.bulk_action": {
"name": "grid.bulk_action",
"category": "interaction",
"description": "Grid 일괄처리 실행",
"allowedAttributes": [
"commandId",
"countBucket"
]
},
"validation.failed": {
"name": "validation.failed",
"category": "quality",
"description": "저장/업무 실행 전후 검증 실패",
"allowedAttributes": [
"stage",
"errorCountBucket",
"reasonCode"
]
},
"excel.import.start": {
"name": "excel.import.start",
"category": "excel",
"description": "Excel Import 시작",
"allowedAttributes": [
"importType",
"rowCountBucket"
]
},
"excel.import.completed": {
"name": "excel.import.completed",
"category": "excel",
"description": "Excel Import 완료",
"allowedAttributes": [
"importType",
"result",
"rowCountBucket"
],
"requiresDuration": true
},
"excel.import.failed": {
"name": "excel.import.failed",
"category": "excel",
"description": "Excel Import 실패",
"allowedAttributes": [
"importType",
"reasonCode",
"rowCountBucket"
],
"requiresDuration": true
},
"exception.open": {
"name": "exception.open",
"category": "exception",
"description": "업무 예외 생성/노출",
"allowedAttributes": [
"exceptionType",
"severity"
]
},
"exception.resolved": {
"name": "exception.resolved",
"category": "exception",
"description": "업무 예외 해결",
"allowedAttributes": [
"exceptionType",
"resolutionType"
],
"requiresDuration": true
},
"ai.proposal.open": {
"name": "ai.proposal.open",
"category": "ai",
"description": "AI 제안 확인",
"allowedAttributes": [
"proposalType"
]
},
"ai.proposal.accept": {
"name": "ai.proposal.accept",
"category": "ai",
"description": "AI 제안 승인",
"allowedAttributes": [
"proposalType"
]
},
"ai.proposal.reject": {
"name": "ai.proposal.reject",
"category": "ai",
"description": "AI 제안 거절",
"allowedAttributes": [
"proposalType",
"reasonCode"
]
},
"manual.intervention": {
"name": "manual.intervention",
"category": "outcome",
"description": "정상 자동처리 대신 사람의 확인/수정/판단이 필요했던 업무",
"allowedAttributes": [
"workType",
"reasonCode",
"exceptionType"
]
},
"experiment.exposed": {
"name": "experiment.exposed",
"category": "experiment",
"description": "실험 Variant가 실제 화면에 렌더링되어 사용자에게 노출됨",
"allowedAttributes": [
"surface"
]
}
} as const
export type KbxTelemetryEventName = keyof typeof kbxTelemetryCatalog
export const kbxUxMetricCatalog = {
"task_completion_time_ms": {
"key": "task_completion_time_ms",
"label": "Task Completion Time",
"numerator": "task.complete.duration_ms",
"aggregation": "p50,p95",
"unit": "ms"
},
"semantic_interactions_per_task": {
"key": "semantic_interactions_per_task",
"label": "Interactions / Task",
"numerator": "interaction.execute",
"denominator": "task.complete",
"aggregation": "ratio",
"unit": "count"
},
"manual_intervention_rate": {
"key": "manual_intervention_rate",
"label": "Manual Intervention Rate",
"numerator": "ux_business_outcomes.manual_intervention_count",
"denominator": "ux_business_outcomes.observed_count",
"aggregation": "ratio",
"unit": "percent"
},
"validation_failure_rate": {
"key": "validation_failure_rate",
"label": "Validation Failure Rate",
"numerator": "validation.failed",
"denominator": "command.execute",
"aggregation": "ratio",
"unit": "percent"
},
"import_failure_rate": {
"key": "import_failure_rate",
"label": "Import Failure Rate",
"numerator": "excel.import.failed",
"denominator": "excel.import.start",
"aggregation": "ratio",
"unit": "percent"
},
"exception_resolution_time_ms": {
"key": "exception_resolution_time_ms",
"label": "Exception Resolution Time",
"numerator": "exception.resolved.duration_ms",
"aggregation": "p50,p95",
"unit": "ms"
},
"ai_proposal_acceptance_rate": {
"key": "ai_proposal_acceptance_rate",
"label": "AI Proposal Acceptance Rate",
"numerator": "ai.proposal.accept",
"denominator": "ai.proposal.open",
"aggregation": "ratio",
"unit": "percent"
}
} as const
export type KbxUxMetricKey = keyof typeof kbxUxMetricCatalog
export const kbxTelemetrySourceSha256 = '9f5de69498b657cb0d60df18c1e98c504a44eec555c4fc50d46f15f83a4ea1ce' as const
@@ -0,0 +1,91 @@
import type { KbxLookupDefinition } from './lookup'
export type KbxGridType =
| 'text' | 'code' | 'integer' | 'decimal' | 'quantity' | 'money'
| 'percent' | 'date' | 'datetime' | 'boolean' | 'status' | 'lookup' | 'link'
export type KbxGridDensity = 'compact' | 'comfortable'
export interface KbxGridColumn<T> {
field: keyof T & string
header: string
type?: KbxGridType
width?: number
minWidth?: number
maxWidth?: number
pinned?: 'left' | 'right'
editable?: boolean | ((row: T) => boolean)
sortable?: boolean
filterable?: boolean
permission?: string
lookup?: KbxLookupDefinition
drilldown?: boolean
}
export interface KbxGridColumnPreference {
field: string
order: number
width?: number
pinned?: 'left' | 'right' | null
hidden?: boolean
sort?: 'asc' | 'desc' | null
sortIndex?: number | null
}
export interface KbxGridCellRef<T = string> {
rowKey: T
field: string
}
export interface KbxGridSummary<T> {
key: string
label: string
field?: keyof T & string
kind: 'count' | 'sum' | 'custom'
value?: string | number
}
/** Browser에는 현재 선택 상태만 유지한다. all-filtered에서 전체 ID를 적재하지 않는다. */
export interface KbxSelectionState<TId = string> {
mode: 'explicit' | 'all-filtered'
selectedIds: TId[]
excludedIds?: TId[]
}
/** Server-side bulk command의 canonical selection envelope. */
export interface KbxBulkSelectionRequest<TFilter = Record<string, unknown>, TId = string> {
mode: 'ids' | 'filter'
ids?: TId[]
filter?: TFilter
excludedIds?: TId[]
}
export interface KbxGridEditingPolicy {
allowRowAdd?: boolean
allowRowDuplicate?: boolean
fillDown?: boolean
paste?: boolean
errorNavigation?: boolean
}
export interface KbxGridPasteIssue {
rowOffset: number
columnOffset: number
field?: string
code: 'INVALID_NUMBER' | 'INVALID_DATE' | 'INVALID_BOOLEAN'
value: unknown
}
export interface KbxGridPasteResult {
cellCount: number
normalizedCount: number
rejectedCount: number
issues: KbxGridPasteIssue[]
}
export interface KbxGridContextRequest<T> {
row: T
field: keyof T & string
clientX: number
clientY: number
}
@@ -0,0 +1,9 @@
export interface KbxHelpContent {
key: string
title: string
purpose: string
steps?: string[]
shortcuts?: { key: string; description: string }[]
cautions?: string[]
relatedScreens?: { id: string; title: string }[]
}
@@ -0,0 +1,59 @@
export * from './screen'
export * from './ui'
export * from './command'
export * from './grid'
export * from './lookup'
export * from './problem'
export * from './field'
export * from './transaction'
export * from './audit'
export * from './wms'
export * from './registry'
export * from './permission'
export * from './help'
export * from './preference'
export * from './status'
export * from './search'
export * from './excel'
export * from './operations'
export * from './suggestion'
export * from './ai'
export * from './workflow'
export * from './runtime'
export * from './performance'
export * from './navigation'
export * from './catalog'
export * from './generated/fieldCatalog'
export * from './api'
export * from './generated/apiCatalog'
export * from './authorization'
export * from './generated/permissionCatalog'
export * from './telemetry'
export * from './generated/telemetryCatalog'
export * from './experiment'
export * from './generated/experimentCatalog'
export * from './testing'
export * from './generated/testScenarioCatalog'
export * from './generated/screenRecipeCatalog'
export * from './integration'
export * from './generated/integrationCatalog'
export * from './provider'
export * from './generated/providerCatalog'
export * from './externalData'
export * from './generated/externalDataCatalog'
export * from './configuration'
export * from './generated/configurationCatalog'
@@ -0,0 +1,58 @@
export type KbxIntegrationState = 'queued' | 'delivering' | 'retrying' | 'delivered' | 'failed' | 'suspended'
export type KbxIntegrationDirection = 'inbound' | 'outbound'
export type KbxIntegrationTransport = 'outbox-http' | 'outbox-event' | 'inbox-event'
export type KbxIntegrationDelivery = 'at-most-once' | 'at-least-once'
export type KbxIntegrationCriticality = 'low' | 'medium' | 'high'
export interface KbxIntegrationShortRetryDefinition {
maxRetryAttempts: number
baseDelayMs: number
backoff: 'constant' | 'exponential'
}
export interface KbxIntegrationLongRetryDefinition {
scheduler: 'hangfire'
maxAttempts: number
scheduleSeconds: readonly number[]
}
export interface KbxIntegrationCircuitBreakerDefinition {
failureRatio: number
samplingSeconds: number
minimumThroughput: number
breakSeconds: number
}
export interface KbxIntegrationDefinition {
id: string
title: string
ownerModule: 'OMS' | 'ERP' | 'WMS' | 'COMMON'
direction: KbxIntegrationDirection
transport: KbxIntegrationTransport
criticality: KbxIntegrationCriticality
sourceEvent: string
target: string
delivery: KbxIntegrationDelivery
ordering: 'none' | 'per-aggregate'
idempotency: 'none' | 'event-id' | 'business-key'
timeoutMs: number
shortRetry: KbxIntegrationShortRetryDefinition
longRetry: KbxIntegrationLongRetryDefinition
circuitBreaker: KbxIntegrationCircuitBreakerDefinition
retryable: readonly string[]
permanent: readonly string[]
terminalAction: 'operations-exception' | 'dead-letter'
userVisible: boolean
}
export interface KbxIntegrationStatusView {
integrationId: string
state: KbxIntegrationState
label: string
attemptCount?: number
lastAttemptAt?: string
nextRetryAt?: string
detail?: string
correlationId?: string
retryAllowed?: boolean
}
@@ -0,0 +1,47 @@
export type KbxLookupColumnSource =
| 'code'
| 'displayName'
| 'secondaryText'
| 'status'
| `metadata.${string}`
export interface KbxLookupColumnDefinition {
key: string
label: string
source: KbxLookupColumnSource
width?: number
align?: 'left' | 'center' | 'right'
}
export interface KbxLookupDefinition {
entity: string
columns?: KbxLookupColumnDefinition[]
pageSize?: number
}
export interface KbxLookupItem<TId = string> {
id: TId
code: string
displayName: string
secondaryText?: string
status?: string
metadata?: Record<string, unknown>
}
export interface KbxLookupSearchRequest {
query?: string
page: number
pageSize: number
filters?: Record<string, unknown>
}
export interface KbxLookupSearchResult<TId = string> {
items: KbxLookupItem<TId>[]
totalCount: number
}
export interface KbxLookupProvider<TId = string> {
search(request: KbxLookupSearchRequest): Promise<KbxLookupSearchResult<TId>>
resolveById(id: TId): Promise<KbxLookupItem<TId> | null>
resolveByCode(code: string): Promise<KbxLookupItem<TId> | null>
}
@@ -0,0 +1,117 @@
export type KbxWorkspaceLaunchMode = 'tab' | 'replace' | 'external'
export type KbxRecentPolicy = 'screen' | 'route' | 'none'
export interface KbxNavigationEntry {
screenId: string
path: string
section: string
group?: string
keywords?: string[]
order?: number
launchMode?: KbxWorkspaceLaunchMode
menuVisible?: boolean
favoriteAllowed?: boolean
/** Persist only a safe catalog path by default. Route mode is opt-in for non-sensitive parameterized routes. */
recentPolicy?: KbxRecentPolicy
/** Menu discovery can be narrower than direct screen access. */
permissions?: string[]
/** Lower values are surfaced first in Home quick-start. Omit when the screen should not be promoted. */
homePriority?: number
/** Stable business-oriented group used by Home without inventing dashboard categories. */
homeGroup?: string
}
export interface KbxNavigationResolvedEntry extends KbxNavigationEntry {
title: string
module: 'OMS' | 'ERP' | 'WMS' | 'COMMON'
permissions?: string[]
}
export interface KbxNavigationSection {
key: string
label: string
module?: 'OMS' | 'ERP' | 'WMS' | 'COMMON'
entries: KbxNavigationResolvedEntry[]
}
export interface KbxRecentNavigation {
screenId: string
path: string
title: string
visitedAt: string
}
export interface KbxWorkspaceTab {
key: string
screenId: string
title: string
path: string
dirty?: boolean
pinned?: boolean
openedAt: string
lastActivatedAt: string
}
export interface KbxNavigationPreference {
favorites: string[]
recents: KbxRecentNavigation[]
sideNavCollapsed: boolean
workspaceMaxTabs: number
/** Versioned key used to prevent cross-user/tenant preference leakage. */
scopeKey?: string
}
export type KbxHomeLaunchSource = 'dirty' | 'pinned' | 'open' | 'favorite' | 'recent' | 'priority'
export interface KbxHomeLaunchItem {
/** Stable UI key. Open workspace instances use tabKey so multiple records from one screen can coexist. */
launchKey: string
screenId: string
title: string
module: 'OMS' | 'ERP' | 'WMS' | 'COMMON'
section: string
path: string
source: KbxHomeLaunchSource
sourceLabel: string
tabKey?: string
/** Optional human-readable route instance hint for multiple open records of the same screen. */
instanceLabel?: string
homeGroup?: string
}
export type KbxHomeAttentionSource = 'dirty' | 'operation-failed' | 'notification-urgent' | 'operation-running' | 'notification'
export interface KbxHomeAttentionItem {
key: string
source: KbxHomeAttentionSource
priority: number
/** Used for deterministic newest-first ordering inside the same business priority. */
occurredAt: string
title: string
detail?: string
actionLabel: string
screenId?: string
path?: string
tabKey?: string
operationId?: string
notificationId?: string
}
export interface KbxHomeAttentionQueue {
items: KbxHomeAttentionItem[]
totalCount: number
overflowCount: number
sourceCounts: Partial<Record<KbxHomeAttentionSource, number>>
}
export interface KbxMenuSearchResult {
screenId: string
title: string
module: 'OMS' | 'ERP' | 'WMS' | 'COMMON'
section: string
path: string
keywords: string[]
score: number
}
@@ -0,0 +1,79 @@
export type KbxSeverity = 'info' | 'warning' | 'critical'
export type KbxWorkItemStatus = 'open' | 'claimed' | 'resolved' | 'ignored'
export interface KbxWorkQueueCounter {
key: string
label: string
count: number
severity?: KbxSeverity
}
export interface KbxWorkItemAction {
id: string
label: string
kind: 'navigate' | 'claim' | 'resolve' | 'retry' | 'domain'
permission?: string
permissionMode?: 'hide' | 'disable'
danger?: boolean
}
export interface KbxWorkItem {
id: string
sourceModule: 'OMS' | 'ERP' | 'WMS' | 'COMMON'
sourceType: string
sourceId: string
referenceNo: string
sourceScreenId?: string | null
code: string
title: string
detail?: string
severity: KbxSeverity
status: KbxWorkItemStatus
ownerId?: string | null
ownerName?: string | null
occurredAt: string
dueAt?: string | null
ageMinutes: number
version: number
context?: Record<string, unknown>
actions?: KbxWorkItemAction[]
}
export interface KbxWorkQueueResult {
items: KbxWorkItem[]
totalCount: number
counters: KbxWorkQueueCounter[]
}
export type KbxReconcileStatus = 'matched' | 'mismatch' | 'pending' | 'resolved'
export interface KbxReconcileItem {
id: string
reconcileType: string
referenceNo: string
sourceLabel: string
targetLabel: string
expectedValue: string
actualValue: string
differenceValue?: string | null
reasonCode?: string | null
reasonText?: string | null
status: KbxReconcileStatus
occurredAt: string
sourceId?: string | null
targetId?: string | null
version: number
}
export interface KbxReconcileSummary {
totalCount: number
matchedCount: number
mismatchCount: number
pendingCount: number
resolvedCount: number
}
export interface KbxReconcileResult {
items: KbxReconcileItem[]
summary: KbxReconcileSummary
}
@@ -0,0 +1,16 @@
export interface KbxPerformanceBudget {
name: string
targetMs: number
percentile?: 50 | 95 | 99
note: string
}
// Reference acceptance targets. Teams may tighten them after field telemetry,
// but should not silently loosen them per screen.
export const kbxReferencePerformanceBudgets: KbxPerformanceBudget[] = [
{ name: 'keyboard-feedback', targetMs: 100, note: '키 입력/Focus/로컬 선택 피드백' },
{ name: 'lookup-search', targetMs: 500, percentile: 95, note: '사내망 기준 Lookup 후보 응답' },
{ name: 'filtered-list-search', targetMs: 2000, percentile: 95, note: '일반 업무 조회 첫 결과' },
{ name: 'wms-authoritative-scan', targetMs: 500, percentile: 95, note: '현장망 기준 서버 확정 Scan 응답' },
{ name: 'foreground-operation', targetMs: 3000, note: '이보다 길면 Operation Center/Job 전환 검토' },
]
@@ -0,0 +1,5 @@
export interface KbxPermissionContext {
has(permission: string): boolean
hasAny(permissions: readonly string[]): boolean
hasAll(permissions: readonly string[]): boolean
}
@@ -0,0 +1,12 @@
export type KbxDensity = 'compact' | 'comfortable' | 'touch'
export interface KbxScreenPreference {
screenId: string
screenVersion: string
density?: KbxDensity
grid?: {
columnState?: unknown
pageSize?: number
}
searchDefaults?: Record<string, unknown>
}
@@ -0,0 +1,94 @@
export interface KbxProblemBase {
type: string
title: string
detail?: string | null
correlationId?: string | null
}
export interface KbxValidationError {
field?: string | null
rowKey?: string | null
code: string
message: string
}
export interface KbxValidationProblem extends KbxProblemBase {
type: 'validation'
errors: KbxValidationError[]
}
export interface KbxProblemAction { id: string; label: string }
export interface KbxBusinessProblem extends KbxProblemBase {
type: 'business-rule'
code: string
actions?: KbxProblemAction[]
}
export interface KbxConflictProblem extends KbxProblemBase {
type: 'conflict'
code: string
currentVersion?: number | null
}
export interface KbxPermissionProblem extends KbxProblemBase {
type: 'permission'
code: string
}
export interface KbxNotFoundProblem extends KbxProblemBase {
type: 'not-found'
code: string
}
export interface KbxIntegrationProblem extends KbxProblemBase {
type: 'integration'
code: string
retryable: boolean
}
export interface KbxSystemProblem extends KbxProblemBase {
type: 'system'
code: string
correlationId: string
retryable?: boolean
}
export type KbxProblem =
| KbxValidationProblem
| KbxBusinessProblem
| KbxConflictProblem
| KbxPermissionProblem
| KbxNotFoundProblem
| KbxIntegrationProblem
| KbxSystemProblem
const problemTypes = new Set<KbxProblem['type']>([
'validation', 'business-rule', 'conflict', 'permission', 'not-found', 'integration', 'system',
])
export function isKbxProblem(value: unknown): value is KbxProblem {
if (!value || typeof value !== 'object') return false
const problem = value as Record<string, unknown>
return typeof problem.type === 'string'
&& problemTypes.has(problem.type as KbxProblem['type'])
&& typeof problem.title === 'string'
}
export function kbxUnexpectedProblem(correlationId: string, detail?: string, retryable = false): KbxSystemProblem {
return {
type: 'system',
code: 'UNEXPECTED_ERROR',
title: '요청을 처리하지 못했습니다.',
detail,
correlationId,
retryable,
}
}
export function isRetryableKbxProblem(value: unknown): boolean {
return isKbxProblem(value)
&& ((value.type === 'system' && value.retryable === true)
|| (value.type === 'integration' && value.retryable === true))
}
@@ -0,0 +1,24 @@
export type KbxExternalProviderPurpose = 'market-data-readonly' | 'disclosure-data-readonly'
export interface KbxExternalProviderOperationDefinition {
id: string
method: string
path: string
response: string
trId?: string
query?: readonly string[]
authOperation?: boolean
notes?: string
}
export interface KbxExternalProviderDefinition {
id: string
title: string
ownerModule: 'COMMON' | 'OMS' | 'ERP' | 'WMS'
purpose: KbxExternalProviderPurpose
mutationAllowed: boolean
officialSources: readonly string[]
official: Record<string, unknown>
kbxPolicy: Record<string, unknown>
operations: readonly KbxExternalProviderOperationDefinition[]
}
@@ -0,0 +1,26 @@
import type { KbxScreenDefinition } from './screen'
export interface KbxScreenManifestEntry {
id: string
version: string
module: KbxScreenDefinition['module']
type: KbxScreenDefinition['type']
templateCode: KbxScreenDefinition['templateCode']
title: string
route?: string
helpKey?: string
requiredPermissions: string[]
}
export interface KbxComponentManifestEntry {
name: string
category: 'primitive' | 'business' | 'template' | 'wms' | 'shell'
purpose: string
allowedUse?: string[]
forbiddenUse?: string[]
keyboard?: string[]
accessibility?: string[]
examples?: string[]
owner: 'KBX'
version: string
}
@@ -0,0 +1,87 @@
export type KbxRuntimeMode = 'normal' | 'degraded' | 'read-only' | 'offline'
export interface KbxRuntimeNotice {
mode: KbxRuntimeMode
title: string
message?: string
since?: string
correlationId?: string
retryAllowed?: boolean
}
export interface KbxDataFreshness {
observedAt: string
staleAfterSeconds?: number
source?: string
}
export type KbxOperationStatus =
| 'queued'
| 'running'
| 'completed'
| 'partially-completed'
| 'failed'
| 'cancelled'
export interface KbxOperationRun {
id: string
type: string
title: string
sourceScreenId?: string
status: KbxOperationStatus
requestedAt: string
startedAt?: string
completedAt?: string
processed?: number
total?: number
succeeded?: number
failed?: number
correlationId?: string
resultMessage?: string
}
export type KbxNotificationSeverity = 'info' | 'success' | 'warning' | 'error'
export interface KbxUserNotification {
id: string
severity: KbxNotificationSeverity
title: string
message?: string
createdAt: string
readAt?: string
screenId?: string
entityType?: string
entityId?: string
action?: {
id: string
label: string
route?: string
}
}
export interface KbxConflictFieldChange {
field: string
label: string
mine?: unknown
latest?: unknown
}
export interface KbxConflictSnapshot {
code: string
title: string
detail?: string
entityId?: string
requestedVersion?: number
currentVersion?: number
changes?: KbxConflictFieldChange[]
correlationId?: string
}
export interface KbxDiagnosticReference {
correlationId: string
requestId?: string
occurredAt: string
screenId?: string
screenVersion?: string
appVersion?: string
}
@@ -0,0 +1,120 @@
import type { KbxCommandDefinition } from './command'
export type KbxScreenType =
| 'list'
| 'master'
| 'transaction'
| 'fast-entry'
| 'master-detail'
| 'queue'
| 'reconcile'
| 'import'
| 'wms-mobile'
export type KbxTemplateMetricTone = 'default' | 'info' | 'success' | 'warning' | 'danger'
export type KbxTemplateStateCapability =
| 'idle'
| 'loading'
| 'refreshing'
| 'empty'
| 'error'
| 'permission'
| 'dirty'
| 'conflict'
| 'validation'
| 'job'
| 'network'
export interface KbxTemplateMetric {
key: string
label: string
value: string | number
tone?: KbxTemplateMetricTone
emphasis?: boolean
}
export interface KbxTemplateContext {
label?: string
hint?: string
updatedAt?: string
metrics?: KbxTemplateMetric[]
}
export interface KbxTemplateManifestEntry {
code: 'T01'|'T02'|'T03'|'T04'|'T05'|'T06'|'T07'|'T08'|'T09'
type: KbxScreenType
component: string
purpose: string
requiredSurfaces: string[]
optionalSurfaces: string[]
keyboard: string[]
referenceScreens: string[]
defaultDensity: 'compact' | 'comfortable' | 'touch'
runtimeStates: ('idle'|'loading'|'empty'|'error'|'ready')[]
/** State concerns the template must expose or deliberately delegate to a shared KBX component. */
stateCapabilities: KbxTemplateStateCapability[]
utilitySurfaces: ('help'|'ai'|'suggestion'|'audit')[]
accessibility: string[]
/** Canonical KBX components expected for a production-grade implementation of this template. */
coreComponents: string[]
/** Review items that must be resolved before a screen of this type is considered complete. */
completionChecks: string[]
}
export type KbxScreenTemplateCode = 'T01'|'T02'|'T03'|'T04'|'T05'|'T06'|'T07'|'T08'|'T09'
export type KbxRecipePermissionKind = 'none'|'write'|'execute'
export interface KbxScreenRecipeCommand {
id: string
label: string
group: 'query'|'edit'|'workflow'|'output'|'more'
shortcut?: string
variant?: 'primary'|'secondary'|'danger'|'ghost'
permissionKind: KbxRecipePermissionKind
}
export interface KbxScreenRecipeTestProfile {
/** Scenario kinds that must exist in the canonical recipe coverage. */
requiredScenarioKinds: ('e2e'|'integration'|'contract')[]
/** Behavioral tags that must be covered by the recipe's canonical scenarios. */
requiredTags: string[]
/** Evidence classes that must be emitted by at least one canonical scenario. */
requiredEvidence: string[]
/** Stable review/test intents generated for every screen using the recipe. */
requiredChecks: string[]
}
export interface KbxScreenRecipeDefinition {
code: KbxScreenTemplateCode
type: KbxScreenType
templateComponent: string
defaultCommands: KbxScreenRecipeCommand[]
requiredPolicies: string[]
recoveryPolicies: string[]
securityPolicies: string[]
canonicalScenarioIds: string[]
scaffoldSurfaces: string[]
testProfile: KbxScreenRecipeTestProfile
}
export interface KbxScreenDefinition {
id: string
version: string
module: 'OMS' | 'ERP' | 'WMS' | 'COMMON'
type: KbxScreenType
/** Explicit template/recipe identity. Type and templateCode must agree. */
templateCode: KbxScreenTemplateCode
title: string
description?: string
permissions?: string[]
commands?: KbxCommandDefinition[]
helpKey?: string
telemetry?: { enabled: boolean }
}
export function defineKbxScreen<T extends KbxScreenDefinition>(definition: T): T {
return definition
}
@@ -0,0 +1,23 @@
import type { KbxLookupDefinition } from './lookup'
export type KbxSearchFieldType = 'text' | 'date' | 'date-range' | 'select' | 'lookup' | 'checkbox'
export interface KbxSearchOption {
value: string
label: string
}
export interface KbxSearchField {
key: string
label: string
type: KbxSearchFieldType
primary?: boolean
width?: 'sm' | 'md' | 'lg'
options?: KbxSearchOption[]
lookup?: KbxLookupDefinition
range?: { from: string; to: string }
placeholder?: string
defaultValue?: unknown
disabled?: boolean
helpText?: string
}
@@ -0,0 +1,16 @@
export type KbxStatusSemantic =
| 'draft'
| 'pending'
| 'processing'
| 'completed'
| 'hold'
| 'warning'
| 'error'
| 'cancelled'
| 'disabled'
export interface KbxStatusDefinition {
value: string
label: string
semantic: KbxStatusSemantic
}
@@ -0,0 +1,23 @@
export type KbxSuggestionCategory = 'inconvenience' | 'bug' | 'improvement'
export interface KbxSuggestionContext {
screenId: string
screenVersion: string
route: string
appVersion: string
userRole: string
activeFilters?: string[]
gridLayoutVersion?: string
}
export interface KbxSuggestionRequest {
category: KbxSuggestionCategory
message: string
includeScreenContext: boolean
context: KbxSuggestionContext
}
export interface KbxSuggestionResult {
suggestionId: string
receivedAt: string
}
@@ -0,0 +1,47 @@
import type { KbxTelemetryEventName } from './generated/telemetryCatalog'
export type KbxTelemetryCategory = 'navigation' | 'task' | 'interaction' | 'command' | 'lookup' | 'quality' | 'excel' | 'exception' | 'ai' | 'outcome' | 'experiment'
export interface KbxUxEvent {
eventName: KbxTelemetryEventName
screenId?: string
screenVersion?: string
sessionId: string
taskSessionId?: string
appVersion?: string
experimentId?: string
experimentVariant?: string
occurredAt: string
durationMs?: number
count?: number
attributes?: Record<string, string>
}
export interface KbxUxEventBatch {
events: KbxUxEvent[]
}
export interface KbxUxMetricRow {
metricKey: string
label: string
value: number | null
unit: 'ms' | 'percent' | 'count'
sampleCount: number
p50?: number | null
p95?: number | null
}
export interface KbxUxMetricsResponse {
from: string
to: string
screenId?: string | null
metrics: KbxUxMetricRow[]
}
export interface KbxUxWorkloadOutcome {
workType: string
observedCount: number
autoProcessedCount: number
manualInterventionCount: number
reasonCode?: string
}
@@ -0,0 +1,32 @@
export type KbxTestScenarioKind = 'e2e' | 'integration' | 'contract'
export type KbxTestIsolation = 'database-reset' | 'tenant-reset' | 'ui-only'
export interface KbxTestScenarioDefinition {
id: string
title: string
screenId: string
kind: KbxTestScenarioKind
tags: ReadonlyArray<string>
fixtureSets: ReadonlyArray<string>
requiredPermissions: ReadonlyArray<string>
apiOperations: ReadonlyArray<string>
isolation: KbxTestIsolation
steps: ReadonlyArray<Record<string, unknown>>
assertions: ReadonlyArray<string>
evidence: ReadonlyArray<string>
}
export interface KbxTestFixtureSetDefinition {
id: string
description: string
refs: ReadonlyArray<string>
}
export interface KbxGeneratedScreenTestPlan {
screenId: string
templateCode: 'T01'|'T02'|'T03'|'T04'|'T05'|'T06'|'T07'|'T08'|'T09'
canonicalScenarioIds: ReadonlyArray<string>
requiredChecks: ReadonlyArray<string>
requiredEvidence: ReadonlyArray<string>
}
@@ -0,0 +1,30 @@
export type KbxTransactionMode = 'new' | 'edit' | 'view'
export interface KbxTransactionContext<THeader, TLine> {
header: THeader
lines: TLine[]
mode: KbxTransactionMode
status: string
dirty: boolean
version?: number
}
export type KbxRecordEditability = 'editable' | 'restricted' | 'readonly'
export interface KbxRecordStatePolicy {
status: string
editability: KbxRecordEditability
editableFields?: string[]
message?: string
}
export function resolveKbxRecordStatePolicy(policies: KbxRecordStatePolicy[], status: string): KbxRecordStatePolicy {
return policies.find(x => x.status === status) ?? { status, editability: 'readonly', message: '현재 상태에서는 수정할 수 없습니다.' }
}
export function kbxFieldReadonly(policy: KbxRecordStatePolicy, field: string): boolean {
if (policy.editability === 'readonly') return true
if (policy.editability === 'editable') return false
return !(policy.editableFields ?? []).includes(field)
}
@@ -0,0 +1,37 @@
export type KbxFieldState = 'default' | 'changed' | 'warning' | 'ai-suggested'
export type KbxAsyncState = 'idle' | 'ready' | 'loading' | 'empty' | 'error'
export interface KbxAsyncStateDefinition {
state: KbxAsyncState
title?: string
detail?: string
actionLabel?: string
}
export interface KbxSummaryItem {
key: string
label: string
value: string | number
emphasis?: boolean
}
export interface KbxQuickFilterItem {
key: string
label: string
count: number
active?: boolean
tone?: 'default' | 'warning' | 'danger'
}
export type KbxShortcutScope = 'application' | 'page' | 'grid' | 'dialog' | 'editor'
export interface KbxShortcutDefinition {
key: string
scope: KbxShortcutScope
priority?: number
enabled?: () => boolean
execute(): void | Promise<void>
}
@@ -0,0 +1,92 @@
export type KbxBarcodeSource = 'keyboard-wedge' | 'camera' | 'manual'
export interface KbxBarcodeEvent {
rawValue: string
normalizedValue: string
source: KbxBarcodeSource
occurredAt: number
}
export type KbxWmsPickingStage =
| 'ready'
| 'await-location'
| 'await-item'
| 'processing'
| 'completed'
| 'blocked'
export interface KbxWmsCurrentLine {
lineId: string
lineNo: number
locationCode: string
itemId: string
itemCode: string
itemName: string
itemOption?: string | null
barcode: string
requiredQty: number
pickedQty: number
remainingQty: number
}
export interface KbxWmsPickingTask {
taskId: string
taskNo: string
stage: KbxWmsPickingStage
status: string
completedLines: number
totalLines: number
completedQty: number
totalQty: number
version: number
currentLine?: KbxWmsCurrentLine | null
message?: string | null
}
export interface KbxWmsScanCommand {
taskId: string
barcode: string
source: KbxBarcodeSource
idempotencyKey: string
expectedVersion: number
occurredAt: string
}
export interface KbxWmsScanResult {
accepted: boolean
duplicate: boolean
task: KbxWmsPickingTask
feedback: 'success' | 'warning' | 'error' | 'neutral'
message: string
}
export type KbxWmsExceptionType =
| 'no-stock'
| 'short-quantity'
| 'wrong-location'
| 'damaged-item'
| 'barcode-issue'
| 'other'
export interface KbxWmsExceptionCommand {
taskId: string
lineId?: string | null
type: KbxWmsExceptionType
memo?: string | null
idempotencyKey: string
expectedVersion: number
}
export interface KbxWmsNetworkState {
online: boolean
pendingCommands: number
syncing: boolean
}
export interface KbxWmsSetQuantityCommand {
taskId: string
lineId: string
pickedQty: number
idempotencyKey: string
expectedVersion: number
}
@@ -0,0 +1,30 @@
import type { KbxStatusSemantic } from './status'
export interface KbxWorkflowStateDefinition {
value: string
label: string
semantic: KbxStatusSemantic
terminal?: boolean
}
export interface KbxWorkflowTransitionDefinition {
id: string
from: string[]
to: string
label: string
permission?: string
confirm?: boolean
reasonRequired?: boolean
}
export interface KbxWorkflowDefinition {
id: string
version: string
states: KbxWorkflowStateDefinition[]
transitions: KbxWorkflowTransitionDefinition[]
}
export function allowedKbxTransitions(workflow: KbxWorkflowDefinition, current: string) {
return workflow.transitions.filter(x => x.from.includes(current))
}