package markitect import ( "fmt" "slices" "github.com/netkingdom/flex-auth/internal/registry" "github.com/netkingdom/flex-auth/pkg/api" ) const ( // SystemID is the protected-system id emitted by Markitect manifests. SystemID = "markitect-tool" namespaceVersion = "markitect-resource-namespace-v1" namespaceDoc = "docs/markitect-resource-namespace.md" ) // Diagnostic describes a Markitect manifest import finding. type Diagnostic struct { Code string `json:"code"` Severity string `json:"severity"` Message string `json:"message"` Resource string `json:"resource,omitempty"` Fields []string `json:"fields,omitempty"` } // ImportResult captures the normalized manifest and diagnostics. type ImportResult struct { Manifest api.ResourceManifest `json:"manifest"` Diagnostics []Diagnostic `json:"diagnostics,omitempty"` } type classification struct { scopeLevel api.ScopeLevel planes []api.Plane parentTypes []string } var classifications = map[string]classification{ "knowledge_base": { scopeLevel: api.ScopeLevelWorkspace, planes: []api.Plane{api.PlaneIntent, api.PlaneData}, }, "repository": { scopeLevel: api.ScopeLevelProject, planes: []api.Plane{api.PlaneBuild, api.PlaneData}, parentTypes: []string{"knowledge_base"}, }, "document": { scopeLevel: api.ScopeLevelResource, planes: []api.Plane{api.PlaneData}, parentTypes: []string{"repository", "knowledge_base"}, }, "section": { scopeLevel: api.ScopeLevelSubresource, planes: []api.Plane{api.PlaneData}, parentTypes: []string{"document"}, }, "span": { scopeLevel: api.ScopeLevelField, planes: []api.Plane{api.PlaneData}, parentTypes: []string{"section", "document"}, }, "context_package": { scopeLevel: api.ScopeLevelDataset, planes: []api.Plane{api.PlaneIntent, api.PlaneData, api.PlanePolicy}, parentTypes: []string{"knowledge_base", "repository", "document"}, }, "workflow_artifact": { scopeLevel: api.ScopeLevelProcess, planes: []api.Plane{api.PlaneExecution, api.PlaneData, api.PlaneAudit}, parentTypes: []string{"context_package", "document"}, }, "export": { scopeLevel: api.ScopeLevelRecord, planes: []api.Plane{api.PlaneData, api.PlaneAudit}, parentTypes: []string{"workflow_artifact", "context_package", "document"}, }, } // ImportResourceManifest validates, enriches, and imports a Markitect manifest. func ImportResourceManifest(store *registry.Store, manifest api.ResourceManifest) (ImportResult, error) { if store == nil { return ImportResult{}, fmt.Errorf("registry store is required") } result := ImportResult{ Manifest: EnrichResourceManifest(manifest), Diagnostics: ValidateResourceManifest(manifest), } if hasError(result.Diagnostics) { return result, fmt.Errorf("markitect resource manifest has import errors") } if err := store.ImportResourceManifest(result.Manifest); err != nil { return result, err } return result, nil } // ValidateResourceManifest returns Markitect-specific import diagnostics. func ValidateResourceManifest(manifest api.ResourceManifest) []Diagnostic { var diagnostics []Diagnostic if manifest.ID == "" { diagnostics = append(diagnostics, errorDiagnostic("MARKITECT-MANIFEST-ID", "", "manifest id is required", "id")) } if manifest.System != SystemID { diagnostics = append(diagnostics, errorDiagnostic("MARKITECT-SYSTEM", "", fmt.Sprintf("manifest system must be %q", SystemID), "system")) } if manifest.Metadata["flex_auth_contract"] != api.FlexAuthContractV0 { diagnostics = append(diagnostics, errorDiagnostic("MARKITECT-CONTRACT", "", "metadata.flex_auth_contract must declare resource-registration-v0", "metadata.flex_auth_contract")) } if manifest.CaringProfile == "" { diagnostics = append(diagnostics, warningDiagnostic("MARKITECT-CARING-PROFILE", "", "missing caring_profile; importer will default to caring-0.4.0-rc2", "caring_profile")) } else if manifest.CaringProfile != api.CaringProfileCaring040RC2 { diagnostics = append(diagnostics, errorDiagnostic("MARKITECT-CARING-PROFILE", "", fmt.Sprintf("unsupported caring_profile %q", manifest.CaringProfile), "caring_profile")) } resourceTypes := make(map[string]string, len(manifest.Resources)) for _, resource := range manifest.Resources { resourceTypes[resource.ID] = resource.Type } for _, resource := range manifest.Resources { diagnostics = append(diagnostics, validateResource(resource, resourceTypes)...) } return diagnostics } // EnrichResourceManifest adds namespace and CARING classification metadata. func EnrichResourceManifest(manifest api.ResourceManifest) api.ResourceManifest { out := manifest if out.CaringProfile == "" { out.CaringProfile = api.CaringProfileCaring040RC2 } out.Metadata = copyMap(out.Metadata) out.Metadata["markitect_namespace"] = namespaceVersion out.Metadata["markitect_namespace_doc"] = namespaceDoc out.Resources = append([]api.Resource(nil), manifest.Resources...) for i, resource := range out.Resources { if class, ok := classifications[resource.Type]; ok { resource.Attributes = copyMap(resource.Attributes) resource.Attributes["markitect_resource_type"] = resource.Type resource.Attributes["caring_scope_level"] = class.scopeLevel resource.Attributes["caring_planes"] = append([]api.Plane(nil), class.planes...) out.Resources[i] = resource } } return out } func validateResource(resource api.Resource, resourceTypes map[string]string) []Diagnostic { var diagnostics []Diagnostic if resource.ID == "" { diagnostics = append(diagnostics, errorDiagnostic("MARKITECT-RESOURCE-ID", "", "resource id is required", "resources[].id")) } if resource.Type == "" { diagnostics = append(diagnostics, errorDiagnostic("MARKITECT-RESOURCE-TYPE", resource.ID, "resource type is required", "resources[].type")) return diagnostics } class, ok := classifications[resource.Type] if !ok { diagnostics = append(diagnostics, errorDiagnostic("MARKITECT-RESOURCE-TYPE", resource.ID, fmt.Sprintf("unknown Markitect resource type %q", resource.Type), "resources[].type")) return diagnostics } if resource.Parent != "" { if parentType, ok := resourceTypes[resource.Parent]; ok && !slices.Contains(class.parentTypes, parentType) { diagnostics = append(diagnostics, errorDiagnostic("MARKITECT-PARENT-TYPE", resource.ID, fmt.Sprintf("resource type %q cannot be parented by %q", resource.Type, parentType), "resources[].parent")) } } if resource.Parent == "" && len(class.parentTypes) > 0 { diagnostics = append(diagnostics, warningDiagnostic("MARKITECT-PARENT-MISSING", resource.ID, fmt.Sprintf("resource type %q usually declares a parent", resource.Type), "resources[].parent")) } if len(resource.Labels) == 0 { diagnostics = append(diagnostics, warningDiagnostic("MARKITECT-LABELS-MISSING", resource.ID, "resource has no labels; CARING exposure classification may be ambiguous", "resources[].labels")) } if resource.TrustZone == "" { diagnostics = append(diagnostics, warningDiagnostic("MARKITECT-TRUST-ZONE-MISSING", resource.ID, "resource has no trust_zone; CARING exposure classification may be ambiguous", "resources[].trust_zone")) } return diagnostics } func errorDiagnostic(code, resource, message string, fields ...string) Diagnostic { return Diagnostic{Code: code, Severity: "error", Resource: resource, Message: message, Fields: fields} } func warningDiagnostic(code, resource, message string, fields ...string) Diagnostic { return Diagnostic{Code: code, Severity: "warning", Resource: resource, Message: message, Fields: fields} } func hasError(diagnostics []Diagnostic) bool { for _, diagnostic := range diagnostics { if diagnostic.Severity == "error" { return true } } return false } func copyMap(in map[string]any) map[string]any { out := make(map[string]any, len(in)) for key, value := range in { out[key] = value } return out }