Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a06e82-3e08-7042-a79d-438ac6eed8db
1988 lines
64 KiB
Python
1988 lines
64 KiB
Python
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
import re
|
|
from typing import Any
|
|
|
|
import yaml
|
|
|
|
|
|
REQUIRED_TOP_LEVEL_FILES = (
|
|
"README.md",
|
|
"INTENT.md",
|
|
"SCOPE.md",
|
|
"canon.yaml",
|
|
"pyproject.toml",
|
|
"workplans/index.yaml",
|
|
"infospace/infospace.yaml",
|
|
"infospace/artifacts/index.yaml",
|
|
)
|
|
|
|
REQUIRED_INFOSPACE_DIRS = (
|
|
"kernel",
|
|
"models",
|
|
"standards",
|
|
"profiles",
|
|
"patterns",
|
|
"mappings",
|
|
"assimilation",
|
|
"schemas",
|
|
"views",
|
|
"agent",
|
|
"examples",
|
|
"validation",
|
|
"indexes",
|
|
)
|
|
|
|
OPTIONAL_COLLECTION_DIRS = (
|
|
"profiles",
|
|
"patterns",
|
|
"mappings",
|
|
"assimilation",
|
|
"examples",
|
|
)
|
|
|
|
REQUIRED_SCHEMAS = (
|
|
"standard.schema.yaml",
|
|
"concept.schema.yaml",
|
|
"mapping.schema.yaml",
|
|
"profile.schema.yaml",
|
|
"assimilation.schema.yaml",
|
|
"interface-card.schema.yaml",
|
|
"agent-brief.schema.yaml",
|
|
"workplan.schema.yaml",
|
|
"alignment-review.schema.yaml",
|
|
"capability.schema.yaml",
|
|
"capability-record.schema.yaml",
|
|
"attribute-value-type.schema.yaml",
|
|
"practice-pattern.schema.yaml",
|
|
"emission-cadence.schema.yaml",
|
|
)
|
|
|
|
RETRIEVAL_BRIEF_KINDS = {
|
|
"access-descriptor-set",
|
|
"alignment-review-kit",
|
|
"alignment-review-schema",
|
|
"alignment-review-workflow",
|
|
"alignment-scorecard",
|
|
"benefit-analysis",
|
|
"benchmark-findings",
|
|
"benchmark-workspace",
|
|
"capture-criteria",
|
|
"caring-mapping",
|
|
"comparison-frame",
|
|
"comparison-report",
|
|
"concept-catalog",
|
|
"conformance-pack",
|
|
"consumer-workplan-brief",
|
|
"consumer-workplan-template",
|
|
"evaluation-pack",
|
|
"evaluation-question-set",
|
|
"example",
|
|
"interface-card-expectation",
|
|
"kernel",
|
|
"mapping",
|
|
"mapping-expectation",
|
|
"model",
|
|
"model-extension",
|
|
"model-selection-guide",
|
|
"native-concept-map",
|
|
"pattern",
|
|
"practice-pattern",
|
|
"practice-pattern-scheme",
|
|
"profile-alignment",
|
|
"profile",
|
|
"standard",
|
|
"extension-candidate-set",
|
|
"visualization-example-set",
|
|
}
|
|
|
|
PURPOSE_REQUIRED_ARTIFACT_IDS = {
|
|
"concept-catalog/purpose-demand",
|
|
"example/consumer-purpose-portfolio",
|
|
"mapping/purpose-demand-governance-candidates",
|
|
"model/purpose-demand-extension",
|
|
"pattern/intent-scope-purposes",
|
|
}
|
|
|
|
PURPOSE_REQUIRED_CONCEPTS = {
|
|
"Purpose",
|
|
"ConsumerPurpose",
|
|
"UseCase",
|
|
"DemandSignal",
|
|
"ConsumerNeed",
|
|
"ProducerCapability",
|
|
"PurposeFit",
|
|
"ScopePressure",
|
|
"EvolutionRequest",
|
|
}
|
|
|
|
PURPOSE_REQUIRED_CONSUMERS = {
|
|
"user-engine",
|
|
"railiance-fabric",
|
|
"repo-scoping",
|
|
}
|
|
|
|
USER_ENGINE_EVALUATION_ARTIFACT_IDS = {
|
|
"evaluation/user-engine",
|
|
"evaluation/user-engine/consumer-workplan-brief",
|
|
"evaluation/user-engine/interface-card-expectations",
|
|
"evaluation/user-engine/questions",
|
|
"evaluation/user-engine/small-saas-alignment",
|
|
}
|
|
|
|
USER_ENGINE_QUESTION_DOMAINS = {
|
|
"access-control",
|
|
"data",
|
|
"governance",
|
|
"organization",
|
|
"purposes",
|
|
"security",
|
|
"task",
|
|
}
|
|
|
|
USER_ENGINE_REQUIRED_ENTITY_IDS = {
|
|
"access-role",
|
|
"account",
|
|
"control",
|
|
"evidence",
|
|
"organization-role",
|
|
"policy",
|
|
"principal",
|
|
"subject",
|
|
"team",
|
|
"tenant",
|
|
"user",
|
|
}
|
|
|
|
USER_ENGINE_REQUIRED_EDGE_TYPES = {
|
|
"assigned_role",
|
|
"authenticates_as",
|
|
"belongs_to_tenant",
|
|
"evidenced_by",
|
|
"evaluated_as",
|
|
"governed_by",
|
|
"implemented_by",
|
|
"member_of",
|
|
"scoped_to",
|
|
}
|
|
|
|
RAILIANCE_FABRIC_CONFORMANCE_ARTIFACT_IDS = {
|
|
"conformance/railiance-fabric",
|
|
"conformance/railiance-fabric/consumer-workplan-brief",
|
|
"conformance/railiance-fabric/entity-edge-capture-criteria",
|
|
"conformance/railiance-fabric/mapping-expectations",
|
|
"conformance/railiance-fabric/visualization-examples",
|
|
}
|
|
|
|
RAILIANCE_FABRIC_REQUIRED_ENTITY_CATEGORIES = {
|
|
"consumer-purpose",
|
|
"control",
|
|
"datastore",
|
|
"deployment",
|
|
"endpoint",
|
|
"evidence",
|
|
"flow",
|
|
"network-zone",
|
|
"pipeline",
|
|
"policy",
|
|
"runtime-resource",
|
|
"service",
|
|
"software-system",
|
|
"source-repository",
|
|
"task",
|
|
"telemetry-signal",
|
|
}
|
|
|
|
RAILIANCE_FABRIC_REQUIRED_CANONICAL_EDGES = {
|
|
"built_from",
|
|
"creates_task",
|
|
"depends_on",
|
|
"deploys",
|
|
"evidenced_by",
|
|
"exposes",
|
|
"flows_to",
|
|
"governed_by",
|
|
"implements",
|
|
"observed_by",
|
|
"part_of",
|
|
"reads_or_writes",
|
|
}
|
|
|
|
RAILIANCE_FABRIC_REQUIRED_MODELS = {
|
|
"model/data",
|
|
"model/devsecops",
|
|
"model/governance",
|
|
"model/landscape",
|
|
"model/network",
|
|
"model/observability",
|
|
"model/purpose-demand-extension",
|
|
"model/security",
|
|
}
|
|
|
|
REPO_SCOPING_COMPARISON_ARTIFACT_IDS = {
|
|
"comparison/repo-scoping/canon-benefit-analysis",
|
|
"comparison/repo-scoping/consumer-workplan-brief",
|
|
"comparison/repo-scoping/extension-candidates",
|
|
"comparison/repo-scoping/frame",
|
|
"comparison/repo-scoping/report",
|
|
}
|
|
|
|
REPO_SCOPING_REQUIRED_DOMAINS = {
|
|
"repository-intent",
|
|
"current-scope",
|
|
"future-scope",
|
|
"consumers-and-purposes",
|
|
"decisions-and-evidence",
|
|
"risks-and-regressions",
|
|
"evolution-requests",
|
|
}
|
|
|
|
REPO_SCOPING_REQUIRED_MAPPINGS = {
|
|
"Scope",
|
|
"Ability",
|
|
"Capability",
|
|
"Feature",
|
|
"Evidence",
|
|
"ObservedFact",
|
|
"ReviewDecision",
|
|
"ExpectationGap",
|
|
"DependencyImpactAnalysis",
|
|
}
|
|
|
|
REPO_SCOPING_REQUIRED_EXTENSION_CANDIDATES = {
|
|
"extension/repository-intent-statement",
|
|
"extension/repository-scope-profile",
|
|
"extension/characteristic-claim",
|
|
"extension/evidence-link",
|
|
"extension/source-observation",
|
|
"extension/source-role",
|
|
"extension/utility-relationship",
|
|
"extension/scope-freshness",
|
|
"extension/propagation-impact",
|
|
"extension/scope-md-interface",
|
|
}
|
|
|
|
CARING_K8S_BENCHMARK_ARTIFACT_IDS = {
|
|
"benchmark/caring/kubernetes-rbac",
|
|
"benchmark/caring/kubernetes-rbac/access-descriptors",
|
|
"benchmark/caring/kubernetes-rbac/caring-mapping",
|
|
"benchmark/caring/kubernetes-rbac/findings",
|
|
"benchmark/caring/kubernetes-rbac/native-concepts",
|
|
}
|
|
|
|
CARING_K8S_REQUIRED_NATIVE_CONCEPTS = {
|
|
"Role",
|
|
"ClusterRole",
|
|
"RoleBinding",
|
|
"ClusterRoleBinding",
|
|
"ServiceAccount",
|
|
"Namespace",
|
|
"Verb",
|
|
"Resource",
|
|
"Scope",
|
|
}
|
|
|
|
CARING_K8S_REQUIRED_CASES = {
|
|
"namespace-pod-reader",
|
|
"workload-creator-derived-execution",
|
|
"cluster-secret-reader",
|
|
"namespace-as-tenant-boundary",
|
|
}
|
|
|
|
CARING_K8S_REQUIRED_DESCRIPTOR_CLASSES = {
|
|
"declared_access",
|
|
"effective_access",
|
|
"derived_capability",
|
|
"induced_access",
|
|
}
|
|
|
|
ALIGNMENT_REVIEW_KIT_ARTIFACT_IDS = {
|
|
"review-kit/alignment",
|
|
"review-kit/alignment/model-selection-guide",
|
|
"review-kit/alignment/schema",
|
|
"review-kit/alignment/scorecard",
|
|
"review-kit/alignment/workflow",
|
|
"review-kit/alignment/workplan-template",
|
|
}
|
|
|
|
ALIGNMENT_REVIEW_REQUIRED_PHASES = {
|
|
"intake",
|
|
"surface-selection",
|
|
"mapping",
|
|
"scoring",
|
|
"workplan-proposal",
|
|
"canon-feedback",
|
|
}
|
|
|
|
ALIGNMENT_REVIEW_SCORECARD_DIMENSIONS = {
|
|
"fit",
|
|
"gaps",
|
|
"conflicts",
|
|
"evidence-quality",
|
|
"implementation-priority",
|
|
"canon-pressure",
|
|
"workplan-readiness",
|
|
}
|
|
|
|
ALIGNMENT_REVIEW_REQUIRED_SURFACES = {
|
|
"model/purpose-demand-extension",
|
|
"pattern/intent-scope-purposes",
|
|
"model/access-control",
|
|
"model/organization",
|
|
"model/governance",
|
|
"model/security",
|
|
"model/data",
|
|
"model/landscape",
|
|
"model/devsecops",
|
|
"model/network",
|
|
"model/observability",
|
|
"model/task",
|
|
"standard/tagging",
|
|
"standard/caring",
|
|
"profile/small-saas",
|
|
"benchmark/caring/kubernetes-rbac",
|
|
}
|
|
|
|
ALIGNMENT_REVIEW_TEMPLATE_MARKERS = {
|
|
"## Current Fit",
|
|
"## Target Alignment",
|
|
"## Migration Steps",
|
|
"## Validation",
|
|
"## Open Questions",
|
|
"## Canon Feedback",
|
|
}
|
|
|
|
PRACTICE_PATTERN_REQUIRED_FRONTMATTER = {
|
|
"id",
|
|
"title",
|
|
"type",
|
|
"scheme",
|
|
"status",
|
|
"version",
|
|
"summary",
|
|
}
|
|
|
|
PRACTICE_PATTERN_STATUSES = {
|
|
"draft",
|
|
"candidate",
|
|
"active",
|
|
"deprecated",
|
|
"retired",
|
|
}
|
|
|
|
PRACTICE_PATTERN_REQUIRED_HEADINGS = {
|
|
"## Intent",
|
|
"## Context",
|
|
"## Problem",
|
|
"## Forces",
|
|
"## Solution",
|
|
"## Dynamics",
|
|
"## Invariants",
|
|
"## Evidence",
|
|
"## Consequences",
|
|
"## Known Uses",
|
|
}
|
|
|
|
PRACTICE_PATTERN_ID_RE = re.compile(
|
|
r"^practice-pattern/[a-z0-9]+(?:-[a-z0-9]+)*$"
|
|
)
|
|
PRACTICE_PATTERN_TITLE_RE = re.compile(r"^[A-Z][A-Za-z0-9]*$")
|
|
PRACTICE_PATTERN_VERSION_RE = re.compile(r"^[0-9]+\.[0-9]+(?:\.[0-9]+)?$")
|
|
|
|
|
|
def structural_checks(context: Any) -> dict[str, list[dict[str, Any]]]:
|
|
errors: list[dict[str, Any]] = []
|
|
warnings: list[dict[str, Any]] = []
|
|
|
|
_check_required_top_level_files(context.repo_root, errors)
|
|
_check_required_infospace_dirs(context.infospace_root, errors)
|
|
_check_required_schemas(context.infospace_root, errors)
|
|
_check_capability_catalog(context.infospace_root, errors)
|
|
_check_emission_cadence_contract(context.infospace_root, errors)
|
|
_check_canon_paths(context.repo_root, context.infospace_root, errors)
|
|
_check_artifact_index(context.repo_root, context.infospace_root, errors)
|
|
_check_practice_pattern_assets(
|
|
context.infospace_root,
|
|
context.infospace.artifacts,
|
|
errors,
|
|
)
|
|
_check_agent_assets(context.infospace_root, context.infospace.artifacts, errors)
|
|
_check_purpose_demand_assets(context.infospace_root, context.infospace.artifacts, errors)
|
|
_check_user_engine_evaluation_assets(
|
|
context.infospace_root,
|
|
context.infospace.artifacts,
|
|
errors,
|
|
)
|
|
_check_railiance_fabric_conformance_assets(
|
|
context.infospace_root,
|
|
context.infospace.artifacts,
|
|
errors,
|
|
)
|
|
_check_repo_scoping_comparison_assets(
|
|
context.infospace_root,
|
|
context.infospace.artifacts,
|
|
errors,
|
|
)
|
|
_check_caring_kubernetes_rbac_benchmark_assets(
|
|
context.infospace_root,
|
|
context.infospace.artifacts,
|
|
errors,
|
|
)
|
|
_check_alignment_review_kit_assets(
|
|
context.infospace_root,
|
|
context.infospace.artifacts,
|
|
errors,
|
|
)
|
|
_check_optional_assets(context.infospace_root, warnings)
|
|
|
|
return {"errors": errors, "warnings": warnings}
|
|
|
|
|
|
def _check_practice_pattern_assets(
|
|
infospace_root: Path,
|
|
artifacts: list[Any],
|
|
errors: list[dict[str, Any]],
|
|
) -> None:
|
|
for artifact in artifacts:
|
|
if artifact.kind != "practice-pattern":
|
|
continue
|
|
|
|
path = infospace_root / artifact.path
|
|
frontmatter = _read_markdown_frontmatter(path, errors)
|
|
missing_fields = sorted(
|
|
PRACTICE_PATTERN_REQUIRED_FRONTMATTER - set(frontmatter)
|
|
)
|
|
if missing_fields:
|
|
errors.append(
|
|
{
|
|
"code": "practice_pattern_missing_frontmatter",
|
|
"artifact_id": artifact.id,
|
|
"path": artifact.path,
|
|
"fields": missing_fields,
|
|
}
|
|
)
|
|
|
|
expected = {
|
|
"id": artifact.id,
|
|
"title": artifact.title,
|
|
"type": "practice-pattern",
|
|
"scheme": "practice-pattern/0.1",
|
|
}
|
|
for field, value in expected.items():
|
|
if frontmatter.get(field) != value:
|
|
errors.append(
|
|
{
|
|
"code": "practice_pattern_frontmatter_mismatch",
|
|
"artifact_id": artifact.id,
|
|
"path": artifact.path,
|
|
"field": field,
|
|
"expected": value,
|
|
"actual": frontmatter.get(field),
|
|
}
|
|
)
|
|
|
|
status = frontmatter.get("status")
|
|
if status not in PRACTICE_PATTERN_STATUSES:
|
|
errors.append(
|
|
{
|
|
"code": "practice_pattern_invalid_status",
|
|
"artifact_id": artifact.id,
|
|
"path": artifact.path,
|
|
"status": status,
|
|
}
|
|
)
|
|
|
|
scalar_contracts = (
|
|
("id", PRACTICE_PATTERN_ID_RE),
|
|
("title", PRACTICE_PATTERN_TITLE_RE),
|
|
("version", PRACTICE_PATTERN_VERSION_RE),
|
|
)
|
|
for field, pattern in scalar_contracts:
|
|
value = frontmatter.get(field)
|
|
if not isinstance(value, str) or pattern.fullmatch(value) is None:
|
|
errors.append(
|
|
{
|
|
"code": "practice_pattern_invalid_frontmatter_value",
|
|
"artifact_id": artifact.id,
|
|
"path": artifact.path,
|
|
"field": field,
|
|
"value": value,
|
|
}
|
|
)
|
|
|
|
summary = frontmatter.get("summary")
|
|
if not isinstance(summary, str) or not summary.strip():
|
|
errors.append(
|
|
{
|
|
"code": "practice_pattern_invalid_frontmatter_value",
|
|
"artifact_id": artifact.id,
|
|
"path": artifact.path,
|
|
"field": "summary",
|
|
"value": summary,
|
|
}
|
|
)
|
|
|
|
for field in ("aliases", "uses", "related_patterns", "known_uses"):
|
|
value = frontmatter.get(field, [])
|
|
if not isinstance(value, list) or not all(
|
|
isinstance(item, str) for item in value
|
|
):
|
|
errors.append(
|
|
{
|
|
"code": "practice_pattern_invalid_frontmatter_value",
|
|
"artifact_id": artifact.id,
|
|
"path": artifact.path,
|
|
"field": field,
|
|
"value": value,
|
|
}
|
|
)
|
|
|
|
related_patterns = frontmatter.get("related_patterns", [])
|
|
if isinstance(related_patterns, list):
|
|
for related in related_patterns:
|
|
if isinstance(related, str) and not related.startswith(
|
|
"practice-pattern/"
|
|
):
|
|
errors.append(
|
|
{
|
|
"code": "practice_pattern_invalid_related_pattern",
|
|
"artifact_id": artifact.id,
|
|
"path": artifact.path,
|
|
"value": related,
|
|
}
|
|
)
|
|
|
|
try:
|
|
headings = {
|
|
line.strip()
|
|
for line in path.read_text(encoding="utf-8").splitlines()
|
|
if line.startswith("## ")
|
|
}
|
|
except FileNotFoundError:
|
|
continue
|
|
missing_headings = sorted(PRACTICE_PATTERN_REQUIRED_HEADINGS - headings)
|
|
if missing_headings:
|
|
errors.append(
|
|
{
|
|
"code": "practice_pattern_missing_sections",
|
|
"artifact_id": artifact.id,
|
|
"path": artifact.path,
|
|
"headings": missing_headings,
|
|
}
|
|
)
|
|
|
|
|
|
def _check_required_top_level_files(
|
|
repo_root: Path,
|
|
errors: list[dict[str, Any]],
|
|
) -> None:
|
|
for relative in REQUIRED_TOP_LEVEL_FILES:
|
|
if not (repo_root / relative).is_file():
|
|
errors.append(
|
|
{
|
|
"code": "missing_required_file",
|
|
"path": relative,
|
|
}
|
|
)
|
|
|
|
|
|
def _check_required_infospace_dirs(
|
|
infospace_root: Path,
|
|
errors: list[dict[str, Any]],
|
|
) -> None:
|
|
for relative in REQUIRED_INFOSPACE_DIRS:
|
|
if not (infospace_root / relative).is_dir():
|
|
errors.append(
|
|
{
|
|
"code": "missing_required_infospace_dir",
|
|
"path": str(Path("infospace") / relative),
|
|
}
|
|
)
|
|
|
|
|
|
def _check_capability_catalog(infospace_root: Path, errors: list[dict[str, Any]]) -> None:
|
|
from .capability import check_catalog_contract, load_catalog
|
|
|
|
try:
|
|
errors.extend(check_catalog_contract(load_catalog(infospace_root / "models/capability/capabilities.yaml")))
|
|
except Exception as exc: # pragma: no cover - catalog missing is structural
|
|
errors.append(
|
|
{
|
|
"code": "capability_catalog_unreadable",
|
|
"message": str(exc),
|
|
}
|
|
)
|
|
|
|
|
|
def _check_emission_cadence_contract(
|
|
infospace_root: Path,
|
|
errors: list[dict[str, Any]],
|
|
) -> None:
|
|
from .contracts import emission_errors
|
|
|
|
schema = _read_yaml(infospace_root / "schemas/emission-cadence.schema.yaml", errors)
|
|
example_path = "standards/emission-cadence/examples/qonto-assistant.yaml"
|
|
example = _read_yaml(infospace_root / example_path, errors)
|
|
if isinstance(schema, dict):
|
|
errors.extend(dict(error, path=example_path)
|
|
for error in emission_errors(example, schema))
|
|
|
|
|
|
def _check_required_schemas(
|
|
infospace_root: Path,
|
|
errors: list[dict[str, Any]],
|
|
) -> None:
|
|
for filename in REQUIRED_SCHEMAS:
|
|
if not (infospace_root / "schemas" / filename).is_file():
|
|
errors.append(
|
|
{
|
|
"code": "missing_schema",
|
|
"path": str(Path("infospace") / "schemas" / filename),
|
|
}
|
|
)
|
|
|
|
|
|
def _check_canon_paths(
|
|
repo_root: Path,
|
|
infospace_root: Path,
|
|
errors: list[dict[str, Any]],
|
|
) -> None:
|
|
canon_path = repo_root / "canon.yaml"
|
|
canon = _read_yaml(canon_path, errors)
|
|
if not isinstance(canon, dict):
|
|
return
|
|
|
|
indexed_paths = _artifact_paths_by_path(infospace_root, errors)
|
|
for section in ("kernel", "models", "standards"):
|
|
items = canon.get(section) or []
|
|
if not isinstance(items, list):
|
|
errors.append(
|
|
{
|
|
"code": "invalid_canon_section",
|
|
"section": section,
|
|
"message": "Expected a list.",
|
|
}
|
|
)
|
|
continue
|
|
for item in items:
|
|
if not isinstance(item, dict):
|
|
errors.append(
|
|
{
|
|
"code": "invalid_canon_entry",
|
|
"section": section,
|
|
"message": "Expected a mapping.",
|
|
}
|
|
)
|
|
continue
|
|
path = str(item.get("path") or "")
|
|
if not path:
|
|
errors.append(
|
|
{
|
|
"code": "missing_canon_path",
|
|
"section": section,
|
|
"id": item.get("id"),
|
|
}
|
|
)
|
|
continue
|
|
if not (repo_root / path).is_file():
|
|
errors.append(
|
|
{
|
|
"code": "missing_canon_path_target",
|
|
"section": section,
|
|
"id": item.get("id"),
|
|
"path": path,
|
|
}
|
|
)
|
|
relative_infospace_path = _strip_infospace_prefix(path)
|
|
if relative_infospace_path not in indexed_paths:
|
|
errors.append(
|
|
{
|
|
"code": "canon_path_not_indexed",
|
|
"section": section,
|
|
"id": item.get("id"),
|
|
"path": path,
|
|
}
|
|
)
|
|
|
|
|
|
def _check_artifact_index(
|
|
repo_root: Path,
|
|
infospace_root: Path,
|
|
errors: list[dict[str, Any]],
|
|
) -> None:
|
|
index_path = infospace_root / "artifacts" / "index.yaml"
|
|
index = _read_yaml(index_path, errors)
|
|
if not isinstance(index, dict):
|
|
return
|
|
artifacts = index.get("artifacts")
|
|
if not isinstance(artifacts, list):
|
|
errors.append(
|
|
{
|
|
"code": "invalid_artifact_index",
|
|
"path": "infospace/artifacts/index.yaml",
|
|
"message": "Expected artifacts list.",
|
|
}
|
|
)
|
|
return
|
|
|
|
ids: set[str] = set()
|
|
for artifact in artifacts:
|
|
if not isinstance(artifact, dict):
|
|
errors.append(
|
|
{
|
|
"code": "invalid_artifact_entry",
|
|
"message": "Expected artifact mapping.",
|
|
}
|
|
)
|
|
continue
|
|
artifact_id = str(artifact.get("id") or "")
|
|
if not artifact_id:
|
|
errors.append({"code": "missing_artifact_id"})
|
|
elif artifact_id in ids:
|
|
errors.append(
|
|
{
|
|
"code": "duplicate_artifact_id",
|
|
"artifact_id": artifact_id,
|
|
}
|
|
)
|
|
ids.add(artifact_id)
|
|
|
|
for field in ("path", "kind", "title"):
|
|
if not artifact.get(field):
|
|
errors.append(
|
|
{
|
|
"code": "missing_artifact_field",
|
|
"artifact_id": artifact_id,
|
|
"field": field,
|
|
}
|
|
)
|
|
|
|
relative_path = str(artifact.get("path") or "")
|
|
if relative_path and not (infospace_root / relative_path).is_file():
|
|
errors.append(
|
|
{
|
|
"code": "missing_artifact_path",
|
|
"artifact_id": artifact_id,
|
|
"path": relative_path,
|
|
}
|
|
)
|
|
|
|
provenance = artifact.get("provenance") or {}
|
|
if isinstance(provenance, dict):
|
|
source_path = provenance.get("source_path")
|
|
if isinstance(source_path, str) and source_path:
|
|
if not (repo_root / source_path).is_file():
|
|
errors.append(
|
|
{
|
|
"code": "missing_provenance_source",
|
|
"artifact_id": artifact_id,
|
|
"source_path": source_path,
|
|
}
|
|
)
|
|
|
|
for artifact in artifacts:
|
|
if not isinstance(artifact, dict):
|
|
continue
|
|
artifact_id = str(artifact.get("id") or "")
|
|
relationships = artifact.get("relationships") or []
|
|
if not isinstance(relationships, list):
|
|
errors.append(
|
|
{
|
|
"code": "invalid_relationships",
|
|
"artifact_id": artifact_id,
|
|
"message": "Expected relationship list.",
|
|
}
|
|
)
|
|
continue
|
|
for relationship in relationships:
|
|
if not isinstance(relationship, dict):
|
|
errors.append(
|
|
{
|
|
"code": "invalid_relationship",
|
|
"artifact_id": artifact_id,
|
|
}
|
|
)
|
|
continue
|
|
target = relationship.get("target")
|
|
if target not in ids:
|
|
errors.append(
|
|
{
|
|
"code": "missing_relationship_target",
|
|
"artifact_id": artifact_id,
|
|
"target": target,
|
|
}
|
|
)
|
|
|
|
|
|
def _check_optional_assets(
|
|
infospace_root: Path,
|
|
warnings: list[dict[str, Any]],
|
|
) -> None:
|
|
global_brief = infospace_root / "agent" / "global-agent-brief.md"
|
|
if not global_brief.is_file():
|
|
warnings.append(
|
|
{
|
|
"code": "missing_optional_agent_brief",
|
|
"path": "infospace/agent/global-agent-brief.md",
|
|
}
|
|
)
|
|
|
|
concepts_dir = infospace_root / "concepts"
|
|
if not concepts_dir.is_dir():
|
|
warnings.append(
|
|
{
|
|
"code": "missing_optional_concepts_dir",
|
|
"path": "infospace/concepts",
|
|
}
|
|
)
|
|
|
|
for relative in OPTIONAL_COLLECTION_DIRS:
|
|
directory = infospace_root / relative
|
|
if directory.is_dir() and not _has_substantive_files(directory):
|
|
warnings.append(
|
|
{
|
|
"code": "empty_optional_collection",
|
|
"path": str(Path("infospace") / relative),
|
|
}
|
|
)
|
|
|
|
|
|
def _check_agent_assets(
|
|
infospace_root: Path,
|
|
artifacts: list[Any],
|
|
errors: list[dict[str, Any]],
|
|
) -> None:
|
|
required_files = (
|
|
"agent/global-agent-brief.md",
|
|
"agent/retrieval-index.md",
|
|
"agent/retrieval-index.yaml",
|
|
"agent/retrieval-index.json",
|
|
"agent/templates/canon-interface-card.template.yaml",
|
|
"agent/templates/consumer-brief.template.md",
|
|
"agent/templates/consumer-alignment-workplan.template.md",
|
|
"agent/consumer-briefs/user-engine.md",
|
|
"agent/consumer-briefs/railiance-fabric.md",
|
|
"agent/consumer-briefs/repo-scoping.md",
|
|
)
|
|
for relative in required_files:
|
|
if not (infospace_root / relative).is_file():
|
|
errors.append(
|
|
{
|
|
"code": "missing_agent_asset",
|
|
"path": str(Path("infospace") / relative),
|
|
}
|
|
)
|
|
|
|
retrieval_index = _read_yaml(infospace_root / "agent" / "retrieval-index.yaml", errors)
|
|
artifact_ids = {artifact.id for artifact in artifacts}
|
|
if isinstance(retrieval_index, dict):
|
|
items = retrieval_index.get("items") or []
|
|
if not isinstance(items, list):
|
|
errors.append(
|
|
{
|
|
"code": "invalid_retrieval_index",
|
|
"path": "infospace/agent/retrieval-index.yaml",
|
|
"message": "Expected items list.",
|
|
}
|
|
)
|
|
else:
|
|
indexed_ids = {
|
|
str(item.get("id"))
|
|
for item in items
|
|
if isinstance(item, dict) and item.get("id")
|
|
}
|
|
missing = sorted(artifact_ids - indexed_ids)
|
|
for artifact_id in missing:
|
|
errors.append(
|
|
{
|
|
"code": "artifact_missing_from_retrieval_index",
|
|
"artifact_id": artifact_id,
|
|
}
|
|
)
|
|
|
|
required_brief_artifacts = [
|
|
artifact
|
|
for artifact in artifacts
|
|
if artifact.kind in RETRIEVAL_BRIEF_KINDS
|
|
]
|
|
for artifact in required_brief_artifacts:
|
|
relative = Path("agent") / "briefs" / f"{_safe_id(artifact.id)}.md"
|
|
brief_path = infospace_root / relative
|
|
if not brief_path.is_file():
|
|
errors.append(
|
|
{
|
|
"code": "missing_agent_brief",
|
|
"artifact_id": artifact.id,
|
|
"path": str(Path("infospace") / relative),
|
|
}
|
|
)
|
|
continue
|
|
frontmatter = _read_markdown_frontmatter(brief_path, errors)
|
|
if frontmatter.get("artifact_id") != artifact.id:
|
|
errors.append(
|
|
{
|
|
"code": "agent_brief_artifact_mismatch",
|
|
"artifact_id": artifact.id,
|
|
"path": str(Path("infospace") / relative),
|
|
"value": frontmatter.get("artifact_id"),
|
|
}
|
|
)
|
|
if frontmatter.get("source_path") != artifact.path:
|
|
errors.append(
|
|
{
|
|
"code": "agent_brief_source_path_mismatch",
|
|
"artifact_id": artifact.id,
|
|
"path": str(Path("infospace") / relative),
|
|
"value": frontmatter.get("source_path"),
|
|
"expected": artifact.path,
|
|
}
|
|
)
|
|
|
|
|
|
def _check_purpose_demand_assets(
|
|
infospace_root: Path,
|
|
artifacts: list[Any],
|
|
errors: list[dict[str, Any]],
|
|
) -> None:
|
|
artifact_ids = {artifact.id for artifact in artifacts}
|
|
for artifact_id in sorted(PURPOSE_REQUIRED_ARTIFACT_IDS - artifact_ids):
|
|
errors.append(
|
|
{
|
|
"code": "missing_purpose_demand_artifact",
|
|
"artifact_id": artifact_id,
|
|
}
|
|
)
|
|
|
|
extension_path = (
|
|
infospace_root
|
|
/ "models"
|
|
/ "governance"
|
|
/ "InfoTechCanonPurposeDemandExtension.md"
|
|
)
|
|
frontmatter = _read_markdown_frontmatter(extension_path, errors)
|
|
owned_concepts = set(frontmatter.get("owned_concepts") or [])
|
|
for concept in sorted(PURPOSE_REQUIRED_CONCEPTS - owned_concepts):
|
|
errors.append(
|
|
{
|
|
"code": "missing_purpose_demand_owned_concept",
|
|
"concept": concept,
|
|
"path": str(extension_path),
|
|
}
|
|
)
|
|
|
|
concept_catalog = _read_yaml(infospace_root / "concepts" / "purpose-demand.yaml", errors)
|
|
if isinstance(concept_catalog, dict):
|
|
concepts = concept_catalog.get("concepts") or []
|
|
catalog_titles = {
|
|
str(concept.get("title"))
|
|
for concept in concepts
|
|
if isinstance(concept, dict) and concept.get("title")
|
|
}
|
|
for concept in sorted(PURPOSE_REQUIRED_CONCEPTS - catalog_titles):
|
|
errors.append(
|
|
{
|
|
"code": "missing_purpose_demand_catalog_concept",
|
|
"concept": concept,
|
|
"path": "infospace/concepts/purpose-demand.yaml",
|
|
}
|
|
)
|
|
|
|
examples = _read_yaml(infospace_root / "examples" / "consumer-purpose-portfolio.yaml", errors)
|
|
if isinstance(examples, dict):
|
|
consumers = examples.get("consumers") or []
|
|
consumer_ids = {
|
|
str(consumer.get("id"))
|
|
for consumer in consumers
|
|
if isinstance(consumer, dict) and consumer.get("id")
|
|
}
|
|
for consumer_id in sorted(PURPOSE_REQUIRED_CONSUMERS - consumer_ids):
|
|
errors.append(
|
|
{
|
|
"code": "missing_consumer_purpose_example",
|
|
"consumer": consumer_id,
|
|
"path": "infospace/examples/consumer-purpose-portfolio.yaml",
|
|
}
|
|
)
|
|
for consumer in consumers:
|
|
if not isinstance(consumer, dict):
|
|
continue
|
|
if not consumer.get("purposes"):
|
|
errors.append(
|
|
{
|
|
"code": "consumer_purpose_example_without_purposes",
|
|
"consumer": consumer.get("id"),
|
|
"path": "infospace/examples/consumer-purpose-portfolio.yaml",
|
|
}
|
|
)
|
|
|
|
|
|
def _check_user_engine_evaluation_assets(
|
|
infospace_root: Path,
|
|
artifacts: list[Any],
|
|
errors: list[dict[str, Any]],
|
|
) -> None:
|
|
artifact_ids = {artifact.id for artifact in artifacts}
|
|
for artifact_id in sorted(USER_ENGINE_EVALUATION_ARTIFACT_IDS - artifact_ids):
|
|
errors.append(
|
|
{
|
|
"code": "missing_user_engine_evaluation_artifact",
|
|
"artifact_id": artifact_id,
|
|
}
|
|
)
|
|
|
|
pack = _read_yaml(
|
|
infospace_root / "evaluations" / "user-engine" / "evaluation-pack.yaml",
|
|
errors,
|
|
)
|
|
if isinstance(pack, dict):
|
|
components = pack.get("pack_components") or {}
|
|
if not isinstance(components, dict):
|
|
errors.append(
|
|
{
|
|
"code": "invalid_user_engine_pack_components",
|
|
"path": "infospace/evaluations/user-engine/evaluation-pack.yaml",
|
|
}
|
|
)
|
|
else:
|
|
for component in (
|
|
"questions",
|
|
"interface_card_expectations",
|
|
"small_saas_alignment",
|
|
"consumer_workplan_brief",
|
|
):
|
|
if not components.get(component):
|
|
errors.append(
|
|
{
|
|
"code": "missing_user_engine_pack_component",
|
|
"component": component,
|
|
}
|
|
)
|
|
|
|
questions = _read_yaml(
|
|
infospace_root / "evaluations" / "user-engine" / "questions.yaml",
|
|
errors,
|
|
)
|
|
if isinstance(questions, dict):
|
|
domains = questions.get("question_domains") or []
|
|
domain_ids = {
|
|
str(domain.get("id"))
|
|
for domain in domains
|
|
if isinstance(domain, dict) and domain.get("id")
|
|
}
|
|
for domain_id in sorted(USER_ENGINE_QUESTION_DOMAINS - domain_ids):
|
|
errors.append(
|
|
{
|
|
"code": "missing_user_engine_question_domain",
|
|
"domain": domain_id,
|
|
}
|
|
)
|
|
for domain in domains:
|
|
if isinstance(domain, dict) and not domain.get("questions"):
|
|
errors.append(
|
|
{
|
|
"code": "empty_user_engine_question_domain",
|
|
"domain": domain.get("id"),
|
|
}
|
|
)
|
|
|
|
expectations = _read_yaml(
|
|
infospace_root
|
|
/ "evaluations"
|
|
/ "user-engine"
|
|
/ "interface-card-expectations.yaml",
|
|
errors,
|
|
)
|
|
if isinstance(expectations, dict):
|
|
entity_ids = {
|
|
str(entity.get("id"))
|
|
for entity in expectations.get("expected_entities") or []
|
|
if isinstance(entity, dict) and entity.get("id")
|
|
}
|
|
for entity_id in sorted(USER_ENGINE_REQUIRED_ENTITY_IDS - entity_ids):
|
|
errors.append(
|
|
{
|
|
"code": "missing_user_engine_expected_entity",
|
|
"entity": entity_id,
|
|
}
|
|
)
|
|
edge_types = {
|
|
str(edge.get("type"))
|
|
for edge in expectations.get("expected_edges") or []
|
|
if isinstance(edge, dict) and edge.get("type")
|
|
}
|
|
for edge_type in sorted(USER_ENGINE_REQUIRED_EDGE_TYPES - edge_types):
|
|
errors.append(
|
|
{
|
|
"code": "missing_user_engine_expected_edge",
|
|
"edge": edge_type,
|
|
}
|
|
)
|
|
evidence = expectations.get("evidence_required") or []
|
|
if not isinstance(evidence, list) or not evidence:
|
|
errors.append(
|
|
{
|
|
"code": "missing_user_engine_evidence_expectations",
|
|
"path": "infospace/evaluations/user-engine/interface-card-expectations.yaml",
|
|
}
|
|
)
|
|
|
|
alignment = _read_yaml(
|
|
infospace_root
|
|
/ "evaluations"
|
|
/ "user-engine"
|
|
/ "small-saas-alignment.yaml",
|
|
errors,
|
|
)
|
|
if isinstance(alignment, dict):
|
|
if alignment.get("profile") != "profile/small-saas":
|
|
errors.append(
|
|
{
|
|
"code": "invalid_user_engine_alignment_profile",
|
|
"value": alignment.get("profile"),
|
|
}
|
|
)
|
|
if not alignment.get("profile_requirements"):
|
|
errors.append(
|
|
{
|
|
"code": "missing_user_engine_profile_requirements",
|
|
"path": "infospace/evaluations/user-engine/small-saas-alignment.yaml",
|
|
}
|
|
)
|
|
|
|
brief_path = infospace_root / "evaluations" / "user-engine" / "consumer-workplan-brief.md"
|
|
if not brief_path.is_file():
|
|
errors.append(
|
|
{
|
|
"code": "missing_user_engine_consumer_workplan_brief",
|
|
"path": "infospace/evaluations/user-engine/consumer-workplan-brief.md",
|
|
}
|
|
)
|
|
|
|
|
|
def _check_railiance_fabric_conformance_assets(
|
|
infospace_root: Path,
|
|
artifacts: list[Any],
|
|
errors: list[dict[str, Any]],
|
|
) -> None:
|
|
artifact_ids = {artifact.id for artifact in artifacts}
|
|
for artifact_id in sorted(RAILIANCE_FABRIC_CONFORMANCE_ARTIFACT_IDS - artifact_ids):
|
|
errors.append(
|
|
{
|
|
"code": "missing_railiance_fabric_conformance_artifact",
|
|
"artifact_id": artifact_id,
|
|
}
|
|
)
|
|
|
|
pack = _read_yaml(
|
|
infospace_root
|
|
/ "evaluations"
|
|
/ "railiance-fabric"
|
|
/ "conformance-pack.yaml",
|
|
errors,
|
|
)
|
|
if isinstance(pack, dict):
|
|
components = pack.get("pack_components") or {}
|
|
if not isinstance(components, dict):
|
|
errors.append(
|
|
{
|
|
"code": "invalid_railiance_fabric_pack_components",
|
|
"path": "infospace/evaluations/railiance-fabric/conformance-pack.yaml",
|
|
}
|
|
)
|
|
else:
|
|
for component in (
|
|
"capture_criteria",
|
|
"mapping_expectations",
|
|
"visualization_examples",
|
|
"consumer_workplan_brief",
|
|
):
|
|
if not components.get(component):
|
|
errors.append(
|
|
{
|
|
"code": "missing_railiance_fabric_pack_component",
|
|
"component": component,
|
|
}
|
|
)
|
|
|
|
criteria = _read_yaml(
|
|
infospace_root
|
|
/ "evaluations"
|
|
/ "railiance-fabric"
|
|
/ "entity-edge-capture-criteria.yaml",
|
|
errors,
|
|
)
|
|
if isinstance(criteria, dict):
|
|
entity_categories = {
|
|
str(entity.get("id"))
|
|
for entity in criteria.get("entity_categories") or []
|
|
if isinstance(entity, dict) and entity.get("id")
|
|
}
|
|
for category in sorted(
|
|
RAILIANCE_FABRIC_REQUIRED_ENTITY_CATEGORIES - entity_categories
|
|
):
|
|
errors.append(
|
|
{
|
|
"code": "missing_railiance_fabric_entity_category",
|
|
"category": category,
|
|
}
|
|
)
|
|
canonical_edges = {
|
|
str(edge.get("type"))
|
|
for edge in criteria.get("canonical_edge_categories") or []
|
|
if isinstance(edge, dict) and edge.get("type")
|
|
}
|
|
for edge_type in sorted(
|
|
RAILIANCE_FABRIC_REQUIRED_CANONICAL_EDGES - canonical_edges
|
|
):
|
|
errors.append(
|
|
{
|
|
"code": "missing_railiance_fabric_canonical_edge",
|
|
"edge": edge_type,
|
|
}
|
|
)
|
|
display_edges = criteria.get("display_only_edge_categories") or []
|
|
if not isinstance(display_edges, list) or not display_edges:
|
|
errors.append(
|
|
{
|
|
"code": "missing_railiance_fabric_display_edges",
|
|
"path": "infospace/evaluations/railiance-fabric/entity-edge-capture-criteria.yaml",
|
|
}
|
|
)
|
|
|
|
mappings = _read_yaml(
|
|
infospace_root
|
|
/ "evaluations"
|
|
/ "railiance-fabric"
|
|
/ "mapping-expectations.yaml",
|
|
errors,
|
|
)
|
|
if isinstance(mappings, dict):
|
|
first_models = {
|
|
str(model.get("id"))
|
|
for model in mappings.get("first_models") or []
|
|
if isinstance(model, dict) and model.get("id")
|
|
}
|
|
for model_id in sorted(RAILIANCE_FABRIC_REQUIRED_MODELS - first_models):
|
|
errors.append(
|
|
{
|
|
"code": "missing_railiance_fabric_mapping_model",
|
|
"model": model_id,
|
|
}
|
|
)
|
|
if not mappings.get("candidate_edge_mapping"):
|
|
errors.append(
|
|
{
|
|
"code": "missing_railiance_fabric_candidate_edge_mapping",
|
|
"path": "infospace/evaluations/railiance-fabric/mapping-expectations.yaml",
|
|
}
|
|
)
|
|
|
|
examples = _read_yaml(
|
|
infospace_root
|
|
/ "evaluations"
|
|
/ "railiance-fabric"
|
|
/ "visualization-examples.yaml",
|
|
errors,
|
|
)
|
|
if isinstance(examples, dict):
|
|
example_items = examples.get("examples") or []
|
|
example_ids = {
|
|
str(example.get("id"))
|
|
for example in example_items
|
|
if isinstance(example, dict) and example.get("id")
|
|
}
|
|
if "clean-service-runtime-slice" not in example_ids:
|
|
errors.append(
|
|
{
|
|
"code": "missing_railiance_fabric_clean_visualization_example",
|
|
}
|
|
)
|
|
if not any(example_id.startswith("bad-shape") for example_id in example_ids):
|
|
errors.append(
|
|
{
|
|
"code": "missing_railiance_fabric_bad_shape_example",
|
|
}
|
|
)
|
|
if not examples.get("visualization_rules"):
|
|
errors.append(
|
|
{
|
|
"code": "missing_railiance_fabric_visualization_rules",
|
|
"path": "infospace/evaluations/railiance-fabric/visualization-examples.yaml",
|
|
}
|
|
)
|
|
|
|
brief_path = (
|
|
infospace_root
|
|
/ "evaluations"
|
|
/ "railiance-fabric"
|
|
/ "consumer-workplan-brief.md"
|
|
)
|
|
if not brief_path.is_file():
|
|
errors.append(
|
|
{
|
|
"code": "missing_railiance_fabric_consumer_workplan_brief",
|
|
"path": "infospace/evaluations/railiance-fabric/consumer-workplan-brief.md",
|
|
}
|
|
)
|
|
|
|
|
|
def _check_repo_scoping_comparison_assets(
|
|
infospace_root: Path,
|
|
artifacts: list[Any],
|
|
errors: list[dict[str, Any]],
|
|
) -> None:
|
|
artifact_ids = {artifact.id for artifact in artifacts}
|
|
for artifact_id in sorted(REPO_SCOPING_COMPARISON_ARTIFACT_IDS - artifact_ids):
|
|
errors.append(
|
|
{
|
|
"code": "missing_repo_scoping_comparison_artifact",
|
|
"artifact_id": artifact_id,
|
|
}
|
|
)
|
|
|
|
frame = _read_yaml(
|
|
infospace_root / "evaluations" / "repo-scoping" / "comparison-frame.yaml",
|
|
errors,
|
|
)
|
|
if isinstance(frame, dict):
|
|
domains = frame.get("comparison_domains") or []
|
|
if not isinstance(domains, list):
|
|
errors.append(
|
|
{
|
|
"code": "invalid_repo_scoping_comparison_domains",
|
|
"path": "infospace/evaluations/repo-scoping/comparison-frame.yaml",
|
|
}
|
|
)
|
|
else:
|
|
domain_ids = {
|
|
str(domain.get("id"))
|
|
for domain in domains
|
|
if isinstance(domain, dict) and domain.get("id")
|
|
}
|
|
for domain_id in sorted(REPO_SCOPING_REQUIRED_DOMAINS - domain_ids):
|
|
errors.append(
|
|
{
|
|
"code": "missing_repo_scoping_comparison_domain",
|
|
"domain": domain_id,
|
|
}
|
|
)
|
|
for domain in domains:
|
|
if not isinstance(domain, dict):
|
|
continue
|
|
if not domain.get("questions"):
|
|
errors.append(
|
|
{
|
|
"code": "empty_repo_scoping_comparison_domain",
|
|
"domain": domain.get("id"),
|
|
}
|
|
)
|
|
if not domain.get("canon_anchors"):
|
|
errors.append(
|
|
{
|
|
"code": "missing_repo_scoping_domain_canon_anchors",
|
|
"domain": domain.get("id"),
|
|
}
|
|
)
|
|
|
|
analysis = _read_yaml(
|
|
infospace_root
|
|
/ "evaluations"
|
|
/ "repo-scoping"
|
|
/ "canon-benefit-analysis.yaml",
|
|
errors,
|
|
)
|
|
if isinstance(analysis, dict):
|
|
mappings = analysis.get("candidate_mappings") or []
|
|
if not isinstance(mappings, list):
|
|
errors.append(
|
|
{
|
|
"code": "invalid_repo_scoping_candidate_mappings",
|
|
"path": "infospace/evaluations/repo-scoping/canon-benefit-analysis.yaml",
|
|
}
|
|
)
|
|
else:
|
|
mapped_concepts = {
|
|
str(mapping.get("repo_scoping_concept"))
|
|
for mapping in mappings
|
|
if isinstance(mapping, dict) and mapping.get("repo_scoping_concept")
|
|
}
|
|
for concept in sorted(REPO_SCOPING_REQUIRED_MAPPINGS - mapped_concepts):
|
|
errors.append(
|
|
{
|
|
"code": "missing_repo_scoping_candidate_mapping",
|
|
"concept": concept,
|
|
}
|
|
)
|
|
|
|
direct_reuse = analysis.get("direct_reuse") or []
|
|
if not isinstance(direct_reuse, list) or not direct_reuse:
|
|
errors.append(
|
|
{
|
|
"code": "missing_repo_scoping_direct_reuse",
|
|
"path": "infospace/evaluations/repo-scoping/canon-benefit-analysis.yaml",
|
|
}
|
|
)
|
|
|
|
profile_candidates = analysis.get("profile_candidates") or []
|
|
if not isinstance(profile_candidates, list) or not profile_candidates:
|
|
errors.append(
|
|
{
|
|
"code": "missing_repo_scoping_profile_candidates",
|
|
"path": "infospace/evaluations/repo-scoping/canon-benefit-analysis.yaml",
|
|
}
|
|
)
|
|
|
|
extensions = _read_yaml(
|
|
infospace_root
|
|
/ "evaluations"
|
|
/ "repo-scoping"
|
|
/ "extension-candidates.yaml",
|
|
errors,
|
|
)
|
|
if isinstance(extensions, dict):
|
|
if not extensions.get("candidate_set_status"):
|
|
errors.append(
|
|
{
|
|
"code": "missing_repo_scoping_candidate_set_status",
|
|
"path": "infospace/evaluations/repo-scoping/extension-candidates.yaml",
|
|
}
|
|
)
|
|
candidates = extensions.get("candidates") or []
|
|
if not isinstance(candidates, list):
|
|
errors.append(
|
|
{
|
|
"code": "invalid_repo_scoping_extension_candidates",
|
|
"path": "infospace/evaluations/repo-scoping/extension-candidates.yaml",
|
|
}
|
|
)
|
|
else:
|
|
candidate_ids = {
|
|
str(candidate.get("id"))
|
|
for candidate in candidates
|
|
if isinstance(candidate, dict) and candidate.get("id")
|
|
}
|
|
for candidate_id in sorted(
|
|
REPO_SCOPING_REQUIRED_EXTENSION_CANDIDATES - candidate_ids
|
|
):
|
|
errors.append(
|
|
{
|
|
"code": "missing_repo_scoping_extension_candidate",
|
|
"candidate": candidate_id,
|
|
}
|
|
)
|
|
for candidate in candidates:
|
|
if isinstance(candidate, dict) and not candidate.get("review_question"):
|
|
errors.append(
|
|
{
|
|
"code": "missing_repo_scoping_extension_review_question",
|
|
"candidate": candidate.get("id"),
|
|
}
|
|
)
|
|
|
|
for filename, code in (
|
|
("comparison-report.md", "missing_repo_scoping_comparison_report"),
|
|
("consumer-workplan-brief.md", "missing_repo_scoping_consumer_workplan_brief"),
|
|
):
|
|
path = infospace_root / "evaluations" / "repo-scoping" / filename
|
|
if not path.is_file():
|
|
errors.append(
|
|
{
|
|
"code": code,
|
|
"path": str(Path("infospace") / "evaluations" / "repo-scoping" / filename),
|
|
}
|
|
)
|
|
|
|
|
|
def _check_caring_kubernetes_rbac_benchmark_assets(
|
|
infospace_root: Path,
|
|
artifacts: list[Any],
|
|
errors: list[dict[str, Any]],
|
|
) -> None:
|
|
artifact_ids = {artifact.id for artifact in artifacts}
|
|
for artifact_id in sorted(CARING_K8S_BENCHMARK_ARTIFACT_IDS - artifact_ids):
|
|
errors.append(
|
|
{
|
|
"code": "missing_caring_kubernetes_rbac_benchmark_artifact",
|
|
"artifact_id": artifact_id,
|
|
}
|
|
)
|
|
|
|
benchmark_root = infospace_root / "standards" / "caring" / "benchmarks" / "kubernetes-rbac"
|
|
if not benchmark_root.is_dir():
|
|
errors.append(
|
|
{
|
|
"code": "missing_caring_kubernetes_rbac_benchmark_workspace",
|
|
"path": "infospace/standards/caring/benchmarks/kubernetes-rbac",
|
|
}
|
|
)
|
|
return
|
|
|
|
benchmark = _read_yaml(benchmark_root / "benchmark.yaml", errors)
|
|
if isinstance(benchmark, dict):
|
|
for field in ("source_corpus", "expected_outputs", "review_criteria"):
|
|
items = benchmark.get(field) or []
|
|
if not isinstance(items, list) or not items:
|
|
errors.append(
|
|
{
|
|
"code": "missing_caring_kubernetes_benchmark_field",
|
|
"field": field,
|
|
}
|
|
)
|
|
cases = benchmark.get("cases") or []
|
|
if not isinstance(cases, list):
|
|
errors.append(
|
|
{
|
|
"code": "invalid_caring_kubernetes_benchmark_cases",
|
|
"path": "infospace/standards/caring/benchmarks/kubernetes-rbac/benchmark.yaml",
|
|
}
|
|
)
|
|
else:
|
|
case_ids = {
|
|
str(case.get("id"))
|
|
for case in cases
|
|
if isinstance(case, dict) and case.get("id")
|
|
}
|
|
for case_id in sorted(CARING_K8S_REQUIRED_CASES - case_ids):
|
|
errors.append(
|
|
{
|
|
"code": "missing_caring_kubernetes_benchmark_case",
|
|
"case": case_id,
|
|
}
|
|
)
|
|
|
|
native = _read_yaml(benchmark_root / "native-concepts.yaml", errors)
|
|
if isinstance(native, dict):
|
|
if native.get("namespace_tenant_boundary_warning") is not True:
|
|
errors.append(
|
|
{
|
|
"code": "missing_caring_kubernetes_namespace_warning",
|
|
"path": "infospace/standards/caring/benchmarks/kubernetes-rbac/native-concepts.yaml",
|
|
}
|
|
)
|
|
concepts = native.get("concepts") or []
|
|
if not isinstance(concepts, list):
|
|
errors.append(
|
|
{
|
|
"code": "invalid_caring_kubernetes_native_concepts",
|
|
"path": "infospace/standards/caring/benchmarks/kubernetes-rbac/native-concepts.yaml",
|
|
}
|
|
)
|
|
else:
|
|
native_names = {
|
|
str(concept.get("native"))
|
|
for concept in concepts
|
|
if isinstance(concept, dict) and concept.get("native")
|
|
}
|
|
for concept in sorted(CARING_K8S_REQUIRED_NATIVE_CONCEPTS - native_names):
|
|
errors.append(
|
|
{
|
|
"code": "missing_caring_kubernetes_native_concept",
|
|
"concept": concept,
|
|
}
|
|
)
|
|
|
|
mapping = _read_yaml(benchmark_root / "caring-mapping.yaml", errors)
|
|
if isinstance(mapping, dict):
|
|
if mapping.get("namespace_tenant_boundary_warning") is not True:
|
|
errors.append(
|
|
{
|
|
"code": "missing_caring_kubernetes_mapping_namespace_warning",
|
|
"path": "infospace/standards/caring/benchmarks/kubernetes-rbac/caring-mapping.yaml",
|
|
}
|
|
)
|
|
mappings = mapping.get("mappings") or []
|
|
if not isinstance(mappings, list):
|
|
errors.append(
|
|
{
|
|
"code": "invalid_caring_kubernetes_mappings",
|
|
"path": "infospace/standards/caring/benchmarks/kubernetes-rbac/caring-mapping.yaml",
|
|
}
|
|
)
|
|
else:
|
|
mapped_names = {
|
|
str(item.get("native_concept"))
|
|
for item in mappings
|
|
if isinstance(item, dict) and item.get("native_concept")
|
|
}
|
|
for concept in sorted(CARING_K8S_REQUIRED_NATIVE_CONCEPTS - mapped_names):
|
|
errors.append(
|
|
{
|
|
"code": "missing_caring_kubernetes_mapping",
|
|
"concept": concept,
|
|
}
|
|
)
|
|
analysis_rules = mapping.get("analysis_rules") or []
|
|
if not isinstance(analysis_rules, list) or not analysis_rules:
|
|
errors.append(
|
|
{
|
|
"code": "missing_caring_kubernetes_analysis_rules",
|
|
"path": "infospace/standards/caring/benchmarks/kubernetes-rbac/caring-mapping.yaml",
|
|
}
|
|
)
|
|
|
|
descriptors = _read_yaml(benchmark_root / "access-descriptors.yaml", errors)
|
|
if isinstance(descriptors, dict):
|
|
descriptor_classes = set(descriptors.get("descriptor_classes") or [])
|
|
for descriptor_class in sorted(
|
|
CARING_K8S_REQUIRED_DESCRIPTOR_CLASSES - descriptor_classes
|
|
):
|
|
errors.append(
|
|
{
|
|
"code": "missing_caring_kubernetes_descriptor_class",
|
|
"descriptor_class": descriptor_class,
|
|
}
|
|
)
|
|
descriptor_items = descriptors.get("descriptors") or []
|
|
if not isinstance(descriptor_items, list):
|
|
errors.append(
|
|
{
|
|
"code": "invalid_caring_kubernetes_descriptors",
|
|
"path": "infospace/standards/caring/benchmarks/kubernetes-rbac/access-descriptors.yaml",
|
|
}
|
|
)
|
|
else:
|
|
used_classes = {
|
|
str(item.get("descriptor_class"))
|
|
for item in descriptor_items
|
|
if isinstance(item, dict) and item.get("descriptor_class")
|
|
}
|
|
for descriptor_class in sorted(
|
|
CARING_K8S_REQUIRED_DESCRIPTOR_CLASSES - used_classes
|
|
):
|
|
errors.append(
|
|
{
|
|
"code": "missing_caring_kubernetes_descriptor_example",
|
|
"descriptor_class": descriptor_class,
|
|
}
|
|
)
|
|
required_fields = (
|
|
"subject",
|
|
"scope",
|
|
"plane",
|
|
"capabilities",
|
|
"exposure_mode",
|
|
"lifecycle_state",
|
|
"native_evidence",
|
|
)
|
|
for item in descriptor_items:
|
|
if not isinstance(item, dict):
|
|
continue
|
|
for field in required_fields:
|
|
if not item.get(field):
|
|
errors.append(
|
|
{
|
|
"code": "incomplete_caring_kubernetes_descriptor",
|
|
"descriptor": item.get("id"),
|
|
"field": field,
|
|
}
|
|
)
|
|
|
|
findings = _read_yaml(benchmark_root / "findings-and-canon-pressure.yaml", errors)
|
|
if isinstance(findings, dict):
|
|
for field in ("stable_findings", "gaps", "conflicts", "proposed_changes"):
|
|
items = findings.get(field) or []
|
|
if not isinstance(items, list) or not items:
|
|
errors.append(
|
|
{
|
|
"code": "missing_caring_kubernetes_findings_field",
|
|
"field": field,
|
|
}
|
|
)
|
|
stable_findings = findings.get("stable_findings") or []
|
|
finding_ids = {
|
|
str(finding.get("id"))
|
|
for finding in stable_findings
|
|
if isinstance(finding, dict) and finding.get("id")
|
|
}
|
|
if "finding/namespace-not-tenant-boundary" not in finding_ids:
|
|
errors.append(
|
|
{
|
|
"code": "missing_caring_kubernetes_namespace_finding",
|
|
"path": "infospace/standards/caring/benchmarks/kubernetes-rbac/findings-and-canon-pressure.yaml",
|
|
}
|
|
)
|
|
|
|
|
|
def _check_alignment_review_kit_assets(
|
|
infospace_root: Path,
|
|
artifacts: list[Any],
|
|
errors: list[dict[str, Any]],
|
|
) -> None:
|
|
artifact_ids = {artifact.id for artifact in artifacts}
|
|
for artifact_id in sorted(ALIGNMENT_REVIEW_KIT_ARTIFACT_IDS - artifact_ids):
|
|
errors.append(
|
|
{
|
|
"code": "missing_alignment_review_kit_artifact",
|
|
"artifact_id": artifact_id,
|
|
}
|
|
)
|
|
|
|
kit_root = infospace_root / "agent" / "review-kit"
|
|
manifest = _read_yaml(kit_root / "review-kit.yaml", errors)
|
|
if isinstance(manifest, dict):
|
|
components = manifest.get("components") or {}
|
|
if not isinstance(components, dict):
|
|
errors.append(
|
|
{
|
|
"code": "invalid_alignment_review_kit_components",
|
|
"path": "infospace/agent/review-kit/review-kit.yaml",
|
|
}
|
|
)
|
|
else:
|
|
for component in (
|
|
"workflow",
|
|
"scorecard",
|
|
"model_selection_guide",
|
|
"schema",
|
|
"consumer_workplan_template",
|
|
):
|
|
if not components.get(component):
|
|
errors.append(
|
|
{
|
|
"code": "missing_alignment_review_kit_component",
|
|
"component": component,
|
|
}
|
|
)
|
|
outputs = set(manifest.get("required_outputs") or [])
|
|
for output in (
|
|
"repository_context",
|
|
"selected_canon_surfaces",
|
|
"mapping_findings",
|
|
"scorecard",
|
|
"recommended_workplans",
|
|
"canon_feedback",
|
|
):
|
|
if output not in outputs:
|
|
errors.append(
|
|
{
|
|
"code": "missing_alignment_review_required_output",
|
|
"output": output,
|
|
}
|
|
)
|
|
|
|
workflow = _read_yaml(kit_root / "review-workflow.yaml", errors)
|
|
if isinstance(workflow, dict):
|
|
phases = workflow.get("phases") or []
|
|
if not isinstance(phases, list):
|
|
errors.append(
|
|
{
|
|
"code": "invalid_alignment_review_workflow_phases",
|
|
"path": "infospace/agent/review-kit/review-workflow.yaml",
|
|
}
|
|
)
|
|
else:
|
|
phase_ids = {
|
|
str(phase.get("id"))
|
|
for phase in phases
|
|
if isinstance(phase, dict) and phase.get("id")
|
|
}
|
|
for phase_id in sorted(ALIGNMENT_REVIEW_REQUIRED_PHASES - phase_ids):
|
|
errors.append(
|
|
{
|
|
"code": "missing_alignment_review_workflow_phase",
|
|
"phase": phase_id,
|
|
}
|
|
)
|
|
for phase in phases:
|
|
if not isinstance(phase, dict):
|
|
continue
|
|
if not phase.get("questions"):
|
|
errors.append(
|
|
{
|
|
"code": "empty_alignment_review_phase_questions",
|
|
"phase": phase.get("id"),
|
|
}
|
|
)
|
|
if not phase.get("outputs"):
|
|
errors.append(
|
|
{
|
|
"code": "empty_alignment_review_phase_outputs",
|
|
"phase": phase.get("id"),
|
|
}
|
|
)
|
|
|
|
scorecard = _read_yaml(kit_root / "scorecard.yaml", errors)
|
|
if isinstance(scorecard, dict):
|
|
dimensions = scorecard.get("dimensions") or []
|
|
if not isinstance(dimensions, list):
|
|
errors.append(
|
|
{
|
|
"code": "invalid_alignment_review_scorecard_dimensions",
|
|
"path": "infospace/agent/review-kit/scorecard.yaml",
|
|
}
|
|
)
|
|
else:
|
|
dimension_ids = {
|
|
str(dimension.get("id"))
|
|
for dimension in dimensions
|
|
if isinstance(dimension, dict) and dimension.get("id")
|
|
}
|
|
for dimension in sorted(
|
|
ALIGNMENT_REVIEW_SCORECARD_DIMENSIONS - dimension_ids
|
|
):
|
|
errors.append(
|
|
{
|
|
"code": "missing_alignment_review_scorecard_dimension",
|
|
"dimension": dimension,
|
|
}
|
|
)
|
|
|
|
guide = _read_yaml(kit_root / "model-selection-guide.yaml", errors)
|
|
if isinstance(guide, dict):
|
|
surfaces = guide.get("surfaces") or []
|
|
if not isinstance(surfaces, list):
|
|
errors.append(
|
|
{
|
|
"code": "invalid_alignment_review_selection_surfaces",
|
|
"path": "infospace/agent/review-kit/model-selection-guide.yaml",
|
|
}
|
|
)
|
|
else:
|
|
surface_ids = {
|
|
str(surface.get("id"))
|
|
for surface in surfaces
|
|
if isinstance(surface, dict) and surface.get("id")
|
|
}
|
|
for surface_id in sorted(ALIGNMENT_REVIEW_REQUIRED_SURFACES - surface_ids):
|
|
errors.append(
|
|
{
|
|
"code": "missing_alignment_review_selection_surface",
|
|
"surface": surface_id,
|
|
}
|
|
)
|
|
for surface in surfaces:
|
|
if not isinstance(surface, dict):
|
|
continue
|
|
if not surface.get("use_when"):
|
|
errors.append(
|
|
{
|
|
"code": "missing_alignment_review_surface_use_when",
|
|
"surface": surface.get("id"),
|
|
}
|
|
)
|
|
if not surface.get("review_questions"):
|
|
errors.append(
|
|
{
|
|
"code": "missing_alignment_review_surface_questions",
|
|
"surface": surface.get("id"),
|
|
}
|
|
)
|
|
|
|
schema = _read_yaml(infospace_root / "schemas" / "alignment-review.schema.yaml", errors)
|
|
if isinstance(schema, dict):
|
|
required_fields = set(schema.get("required") or [])
|
|
for field in (
|
|
"repository_context",
|
|
"selected_canon_surfaces",
|
|
"mapping_findings",
|
|
"scorecard",
|
|
"recommended_workplans",
|
|
"canon_feedback",
|
|
):
|
|
if field not in required_fields:
|
|
errors.append(
|
|
{
|
|
"code": "missing_alignment_review_schema_required_field",
|
|
"field": field,
|
|
}
|
|
)
|
|
|
|
template_path = (
|
|
infospace_root / "agent" / "templates" / "consumer-alignment-workplan.template.md"
|
|
)
|
|
try:
|
|
template = template_path.read_text(encoding="utf-8")
|
|
except FileNotFoundError:
|
|
errors.append(
|
|
{
|
|
"code": "missing_alignment_review_workplan_template",
|
|
"path": "infospace/agent/templates/consumer-alignment-workplan.template.md",
|
|
}
|
|
)
|
|
else:
|
|
for marker in sorted(ALIGNMENT_REVIEW_TEMPLATE_MARKERS):
|
|
if marker not in template:
|
|
errors.append(
|
|
{
|
|
"code": "missing_alignment_review_template_marker",
|
|
"marker": marker,
|
|
}
|
|
)
|
|
|
|
|
|
def _artifact_paths_by_path(
|
|
infospace_root: Path,
|
|
errors: list[dict[str, Any]],
|
|
) -> set[str]:
|
|
index = _read_yaml(infospace_root / "artifacts" / "index.yaml", errors)
|
|
if not isinstance(index, dict):
|
|
return set()
|
|
artifacts = index.get("artifacts") or []
|
|
if not isinstance(artifacts, list):
|
|
return set()
|
|
return {
|
|
str(artifact.get("path"))
|
|
for artifact in artifacts
|
|
if isinstance(artifact, dict) and artifact.get("path")
|
|
}
|
|
|
|
|
|
def _read_yaml(path: Path, errors: list[dict[str, Any]]) -> Any:
|
|
try:
|
|
with path.open("r", encoding="utf-8") as handle:
|
|
return yaml.safe_load(handle) or {}
|
|
except FileNotFoundError:
|
|
errors.append({"code": "missing_yaml", "path": str(path)})
|
|
except yaml.YAMLError as exc:
|
|
errors.append(
|
|
{
|
|
"code": "invalid_yaml",
|
|
"path": str(path),
|
|
"message": str(exc),
|
|
}
|
|
)
|
|
return None
|
|
|
|
|
|
def _read_markdown_frontmatter(path: Path, errors: list[dict[str, Any]]) -> dict[str, Any]:
|
|
try:
|
|
text = path.read_text(encoding="utf-8")
|
|
except FileNotFoundError:
|
|
errors.append({"code": "missing_markdown", "path": str(path)})
|
|
return {}
|
|
if not text.startswith("---\n"):
|
|
errors.append({"code": "missing_markdown_frontmatter", "path": str(path)})
|
|
return {}
|
|
end = text.find("\n---\n", 4)
|
|
if end == -1:
|
|
errors.append({"code": "invalid_markdown_frontmatter", "path": str(path)})
|
|
return {}
|
|
try:
|
|
data = yaml.safe_load(text[4:end]) or {}
|
|
except yaml.YAMLError as exc:
|
|
errors.append(
|
|
{
|
|
"code": "invalid_markdown_frontmatter_yaml",
|
|
"path": str(path),
|
|
"message": str(exc),
|
|
}
|
|
)
|
|
return {}
|
|
return data if isinstance(data, dict) else {}
|
|
|
|
|
|
def _strip_infospace_prefix(path: str) -> str:
|
|
prefix = "infospace/"
|
|
return path[len(prefix) :] if path.startswith(prefix) else path
|
|
|
|
|
|
def _has_substantive_files(directory: Path) -> bool:
|
|
for path in directory.rglob("*"):
|
|
if path.is_file() and path.name != "README.md":
|
|
return True
|
|
return False
|
|
|
|
|
|
def _safe_id(value: str) -> str:
|
|
return value.replace("/", "-").replace("_", "-")
|