Add purpose and demand model extension

This commit is contained in:
2026-05-23 04:59:16 +02:00
parent cf8ff12608
commit 82424cc6f4
39 changed files with 1597 additions and 86 deletions

View File

@@ -52,6 +52,44 @@ REQUIRED_SCHEMAS = (
"workplan.schema.yaml",
)
RETRIEVAL_BRIEF_KINDS = {
"concept-catalog",
"example",
"kernel",
"mapping",
"model",
"model-extension",
"pattern",
"profile",
"standard",
}
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",
}
def structural_checks(context: Any) -> dict[str, list[dict[str, Any]]]:
errors: list[dict[str, Any]] = []
@@ -63,6 +101,7 @@ def structural_checks(context: Any) -> dict[str, list[dict[str, Any]]]:
_check_canon_paths(context.repo_root, context.infospace_root, errors)
_check_artifact_index(context.repo_root, context.infospace_root, errors)
_check_agent_assets(context.infospace_root, context.infospace.artifacts, errors)
_check_purpose_demand_assets(context.infospace_root, context.infospace.artifacts, errors)
_check_optional_assets(context.infospace_root, warnings)
return {"errors": errors, "warnings": warnings}
@@ -370,7 +409,7 @@ def _check_agent_assets(
required_brief_artifacts = [
artifact
for artifact in artifacts
if artifact.kind in {"kernel", "model", "standard", "profile"}
if artifact.kind in RETRIEVAL_BRIEF_KINDS
]
for artifact in required_brief_artifacts:
relative = Path("agent") / "briefs" / f"{_safe_id(artifact.id)}.md"
@@ -406,6 +445,83 @@ def _check_agent_assets(
)
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 _artifact_paths_by_path(
infospace_root: Path,
errors: list[dict[str, Any]],