Import Markitect resource manifests
Some checks failed
CI / Build and Test (push) Has been cancelled
CI / Lint (push) Has been cancelled

This commit is contained in:
2026-05-17 06:21:28 +02:00
parent dd4f688ab6
commit 9e2591c1f4
4 changed files with 350 additions and 1 deletions

View File

@@ -0,0 +1,13 @@
id: markitect-ambiguous-example
system: markitect-tool
caring_profile: caring-0.4.0-rc2
resources:
- id: document:ambiguous-note
type: document
parent: knowledge-base:markitect-example
path: examples/policy/ambiguous-note.md
actions:
- read
metadata:
source: examples/markitect/ambiguous_resource_manifest.yaml
flex_auth_contract: resource-registration-v0

View File

@@ -0,0 +1,207 @@
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
}

View File

@@ -0,0 +1,129 @@
package markitect_test
import (
"os"
"path/filepath"
"testing"
"gopkg.in/yaml.v3"
"github.com/netkingdom/flex-auth/internal/markitect"
"github.com/netkingdom/flex-auth/internal/registry"
"github.com/netkingdom/flex-auth/pkg/api"
)
func TestImportResourceManifestLoadsPinnedMarkitectShape(t *testing.T) {
var manifest api.ResourceManifest
loadYAML(t, filepath.Join("..", "..", "examples", "markitect", "resource_manifest.yaml"), &manifest)
store := registry.NewStore()
result, err := markitect.ImportResourceManifest(store, manifest)
if err != nil {
t.Fatalf("ImportResourceManifest: %v", err)
}
if result.Manifest.CaringProfile != api.CaringProfileCaring040RC2 {
t.Fatalf("CaringProfile = %q; want %q", result.Manifest.CaringProfile, api.CaringProfileCaring040RC2)
}
if !hasDiagnostic(result.Diagnostics, "MARKITECT-CARING-PROFILE", "warning") {
t.Fatalf("diagnostics = %+v; want missing CARING profile warning", result.Diagnostics)
}
resource, ok := store.Resource(markitect.SystemID, "document:internal-note")
if !ok {
t.Fatal("document:internal-note was not imported")
}
if resource.Attributes["caring_scope_level"] != api.ScopeLevelResource {
t.Fatalf("caring_scope_level = %v; want Resource", resource.Attributes["caring_scope_level"])
}
}
func TestImportNamespaceResourceManifestClassifiesAllResources(t *testing.T) {
var manifest api.ResourceManifest
loadYAML(t, filepath.Join("..", "..", "examples", "markitect", "namespace_resource_manifest.yaml"), &manifest)
store := registry.NewStore()
result, err := markitect.ImportResourceManifest(store, manifest)
if err != nil {
t.Fatalf("ImportResourceManifest: %v\n%+v", err, result.Diagnostics)
}
if hasSeverity(result.Diagnostics, "error") {
t.Fatalf("diagnostics = %+v; did not expect errors", result.Diagnostics)
}
span, ok := store.Resource(markitect.SystemID, "span:internal-note#risk:customer-email")
if !ok {
t.Fatal("span resource was not imported")
}
if span.Attributes["caring_scope_level"] != api.ScopeLevelField {
t.Fatalf("span caring_scope_level = %v; want Field", span.Attributes["caring_scope_level"])
}
if span.TrustZone != "restricted" {
t.Fatalf("span.TrustZone = %q; want restricted", span.TrustZone)
}
}
func TestImportAmbiguousManifestReportsClassificationWarnings(t *testing.T) {
var manifest api.ResourceManifest
loadYAML(t, filepath.Join("..", "..", "examples", "markitect", "ambiguous_resource_manifest.yaml"), &manifest)
store := registry.NewStore()
result, err := markitect.ImportResourceManifest(store, manifest)
if err != nil {
t.Fatalf("ImportResourceManifest: %v", err)
}
if !hasDiagnostic(result.Diagnostics, "MARKITECT-LABELS-MISSING", "warning") {
t.Fatalf("diagnostics = %+v; want labels warning", result.Diagnostics)
}
if !hasDiagnostic(result.Diagnostics, "MARKITECT-TRUST-ZONE-MISSING", "warning") {
t.Fatalf("diagnostics = %+v; want trust zone warning", result.Diagnostics)
}
}
func TestImportRejectsUnknownMarkitectResourceType(t *testing.T) {
manifest := api.ResourceManifest{
ID: "bad",
System: markitect.SystemID,
CaringProfile: api.CaringProfileCaring040RC2,
Resources: []api.Resource{
{ID: "unknown:1", Type: "unknown_type", Labels: []string{"internal"}, TrustZone: "internal"},
},
Metadata: map[string]any{"flex_auth_contract": api.FlexAuthContractV0},
}
_, err := markitect.ImportResourceManifest(registry.NewStore(), manifest)
if err == nil {
t.Fatal("ImportResourceManifest accepted unknown resource type")
}
}
func hasDiagnostic(diagnostics []markitect.Diagnostic, code, severity string) bool {
for _, diagnostic := range diagnostics {
if diagnostic.Code == code && diagnostic.Severity == severity {
return true
}
}
return false
}
func hasSeverity(diagnostics []markitect.Diagnostic, severity string) bool {
for _, diagnostic := range diagnostics {
if diagnostic.Severity == severity {
return true
}
}
return false
}
func loadYAML(t *testing.T, path string, out any) {
t.Helper()
data, err := os.ReadFile(path)
if err != nil {
t.Fatalf("read %s: %v", path, err)
}
if err := yaml.Unmarshal(data, out); err != nil {
t.Fatalf("unmarshal %s: %v", path, err)
}
}

View File

@@ -62,7 +62,7 @@ each resource level to CARING scope and plane values.
```task ```task
id: FLEX-WP-0003-T002 id: FLEX-WP-0003-T002
status: todo status: done
priority: high priority: high
state_hub_task_id: "90082eaf-37f5-492f-a884-ff8eec0eccaa" state_hub_task_id: "90082eaf-37f5-492f-a884-ff8eec0eccaa"
``` ```