generated from coulomb/repo-seed
Add purpose and demand model extension
This commit is contained in:
@@ -9,7 +9,17 @@ import yaml
|
||||
|
||||
|
||||
GENERATED_NOTICE = "<!-- GENERATED by info_tech_canon; do not edit by hand. -->"
|
||||
RETRIEVAL_ARTIFACT_KINDS = {"kernel", "model", "standard", "profile"}
|
||||
RETRIEVAL_ARTIFACT_KINDS = {
|
||||
"concept-catalog",
|
||||
"example",
|
||||
"kernel",
|
||||
"mapping",
|
||||
"model",
|
||||
"model-extension",
|
||||
"pattern",
|
||||
"profile",
|
||||
"standard",
|
||||
}
|
||||
CONSUMER_BRIEF_IDS = ("user-engine", "railiance-fabric", "repo-scoping")
|
||||
COMMON_DISTINCTIONS = [
|
||||
{
|
||||
@@ -45,10 +55,11 @@ COMMON_DISTINCTIONS = [
|
||||
{
|
||||
"id": "intent-scope-purpose",
|
||||
"title": "Intent vs Scope vs Purpose",
|
||||
"summary": "Intent captures why an actor wants something, scope bounds what is included, and purpose captures consumer demand or use case pressure on the repo.",
|
||||
"summary": "Intent captures why a producer or consumer exists, scope bounds producer ownership and promises, and purpose captures consumer demand anchored in consumer intent.",
|
||||
"source_artifacts": [
|
||||
"kernel/itc-core",
|
||||
"model/governance",
|
||||
"model/purpose-demand-extension",
|
||||
"pattern/intent-scope-purposes",
|
||||
"profile/small-saas",
|
||||
],
|
||||
},
|
||||
@@ -634,7 +645,14 @@ def interface_card_template() -> dict[str, Any]:
|
||||
"owner": "",
|
||||
"intent": "",
|
||||
"scope": "",
|
||||
"purposes": [],
|
||||
"purposes": [
|
||||
{
|
||||
"id": "",
|
||||
"use_case": "",
|
||||
"consumer_need": "",
|
||||
"demand_signals": [],
|
||||
}
|
||||
],
|
||||
},
|
||||
"canon_surfaces": {
|
||||
"implemented_profiles": [],
|
||||
@@ -649,6 +667,12 @@ def interface_card_template() -> dict[str, Any]:
|
||||
"evidence_required": [],
|
||||
"known_gaps": [],
|
||||
},
|
||||
"purpose_fit": {
|
||||
"state": "",
|
||||
"matched_capabilities": [],
|
||||
"scope_pressure": "",
|
||||
"recommended_disposition": "",
|
||||
},
|
||||
"consumer_needs": {
|
||||
"current": [],
|
||||
"requested_extensions": [],
|
||||
@@ -674,6 +698,8 @@ def _render_consumer_brief_template() -> str:
|
||||
"- Intent:",
|
||||
"- Scope:",
|
||||
"- Purposes:",
|
||||
"- Use cases:",
|
||||
"- Demand signals:",
|
||||
"",
|
||||
"## Canon Surfaces",
|
||||
"",
|
||||
@@ -687,6 +713,13 @@ def _render_consumer_brief_template() -> str:
|
||||
"- Commands:",
|
||||
"- Evidence:",
|
||||
"- Known gaps:",
|
||||
"",
|
||||
"## Purpose Fit",
|
||||
"",
|
||||
"- State:",
|
||||
"- Matched producer capabilities:",
|
||||
"- Scope pressure:",
|
||||
"- Requested evolution:",
|
||||
]
|
||||
return "\n".join(lines).rstrip() + "\n"
|
||||
|
||||
@@ -721,6 +754,9 @@ def _render_consumer_brief(consumer_id: str) -> str:
|
||||
"",
|
||||
"- `agent/retrieval-index.md`",
|
||||
"- `agent/templates/canon-interface-card.template.yaml`",
|
||||
"- `models/governance/InfoTechCanonPurposeDemandExtension.md`",
|
||||
"- `patterns/intent-scope-purposes.md`",
|
||||
"- `examples/consumer-purpose-portfolio.yaml`",
|
||||
"- `profiles/small-saas/profile.yaml`",
|
||||
"- `views/by-concept.md`",
|
||||
"",
|
||||
@@ -788,6 +824,16 @@ def _safe_id(value: str) -> str:
|
||||
def _summary_for_artifact(artifact: Any) -> str:
|
||||
if artifact.kind == "profile-artifact":
|
||||
return f"Example artifact for the {artifact.provenance.get('profile', 'unknown')} profile: {artifact.title}."
|
||||
if artifact.kind == "concept-catalog":
|
||||
return f"Structured candidate concept catalog: {artifact.title}."
|
||||
if artifact.kind == "example":
|
||||
return f"Canon-side example artifact: {artifact.title}."
|
||||
if artifact.kind == "mapping":
|
||||
return f"Mapping artifact connecting canon surfaces: {artifact.title}."
|
||||
if artifact.kind == "model-extension":
|
||||
return f"Candidate extension to an existing canon model: {artifact.title}."
|
||||
if artifact.kind == "pattern":
|
||||
return f"Reusable canon pattern: {artifact.title}."
|
||||
if artifact.kind == "profile":
|
||||
return f"Profile that constrains canon artifacts for a practical implementation slice: {artifact.title}."
|
||||
if artifact.kind == "kernel":
|
||||
|
||||
@@ -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]],
|
||||
|
||||
Reference in New Issue
Block a user