129 lines
6.8 KiB
JSON
129 lines
6.8 KiB
JSON
{
|
|
"guards": [
|
|
{
|
|
"cmd": "test -d catalog/taskservs",
|
|
"id": "taskservs_dir_exists",
|
|
"reason": "catalog/taskservs/ directory not found — run from provisioning root",
|
|
"severity": "Block"
|
|
}
|
|
],
|
|
"id": "provisioning-dag-integrity",
|
|
"postconditions": [
|
|
"All taskservs have provides/requires/conflicts_with fields (or MISSING_FIELDS flagged)",
|
|
"All Required capabilities resolve to at least one provider (or UNRESOLVED flagged)",
|
|
"ConflictsWith pairs identified (enforcement delegated to provisioning-validate-formula per-formula)",
|
|
"Capability coverage table emitted"
|
|
],
|
|
"preconditions": [
|
|
"nickel is in PATH",
|
|
"jq is in PATH",
|
|
"catalog/taskservs/ directory exists with at least one metadata.ncl",
|
|
"NICKEL_IMPORT_PATH includes the provisioning/ root"
|
|
],
|
|
"steps": [
|
|
{
|
|
"action": "export_taskserv_metadata",
|
|
"actor": "Agent",
|
|
"cmd": "for ts in catalog/taskservs/*/; do name=$(basename $ts); meta=$ts/metadata.ncl; if [ -f \"$meta\" ]; then echo \"{\\\"name\\\": \\\"$name\\\", \\\"meta\\\": $(nickel export --format json --import-path . $meta 2>/dev/null || echo 'null')}\"; fi; done | jq -s 'map(select(.meta != null))'",
|
|
"depends_on": [],
|
|
"id": "export_all_metadata",
|
|
"note": "Export all taskserv metadata.ncl files as JSON. Filters out taskservs with missing or broken metadata.",
|
|
"on_error": {
|
|
"backoff_s": 5,
|
|
"max": 3,
|
|
"strategy": "Stop"
|
|
}
|
|
},
|
|
{
|
|
"action": "verify_capability_fields_present",
|
|
"actor": "Agent",
|
|
"cmd": "for ts in catalog/taskservs/*/; do name=$(basename $ts); meta=$ts/metadata.ncl; if [ -f \"$meta\" ]; then result=$(nickel export --format json --import-path . $meta 2>/dev/null); if [ $? -ne 0 ]; then echo \"EXPORT_FAIL: $name\"; continue; fi; has_provides=$(echo $result | jq 'has(\"provides\")'); has_requires=$(echo $result | jq 'has(\"requires\")'); has_conflicts=$(echo $result | jq 'has(\"conflicts_with\")'); if [ \"$has_provides\" != 'true' ] || [ \"$has_requires\" != 'true' ] || [ \"$has_conflicts\" != 'true' ]; then echo \"MISSING_FIELDS: $name (provides=$has_provides requires=$has_requires conflicts_with=$has_conflicts)\"; else echo \"OK: $name\"; fi; fi; done",
|
|
"depends_on": [],
|
|
"id": "check_fields_populated",
|
|
"note": "Check that every taskserv metadata.ncl has provides, requires, and conflicts_with fields. MISSING_FIELDS lines indicate incomplete migrations.",
|
|
"on_error": {
|
|
"backoff_s": 5,
|
|
"max": 3,
|
|
"strategy": "Continue"
|
|
}
|
|
},
|
|
{
|
|
"action": "index_all_capabilities",
|
|
"actor": "Agent",
|
|
"cmd": "for ts in catalog/taskservs/*/; do meta=$ts/metadata.ncl; if [ -f \"$meta\" ]; then nickel export --format json --import-path . $meta 2>/dev/null | jq -r --arg ts \"$(basename $ts)\" '.provides[]? | [$ts, .id] | @tsv'; fi; done | sort",
|
|
"depends_on": [
|
|
{
|
|
"kind": "Always",
|
|
"step": "check_fields_populated"
|
|
}
|
|
],
|
|
"id": "build_provides_index",
|
|
"note": "Build the capability provides index: one line per (taskserv, capability_id) pair. Used in resolution step.",
|
|
"on_error": {
|
|
"backoff_s": 5,
|
|
"max": 3,
|
|
"strategy": "Stop"
|
|
}
|
|
},
|
|
{
|
|
"action": "check_required_dep_coverage",
|
|
"actor": "Agent",
|
|
"cmd": "provides=$(for ts in catalog/taskservs/*/; do meta=$ts/metadata.ncl; if [ -f \"$meta\" ]; then nickel export --format json --import-path . $meta 2>/dev/null | jq -r '.provides[]?.id'; fi; done | sort -u); for ts in catalog/taskservs/*/; do name=$(basename $ts); meta=$ts/metadata.ncl; if [ -f \"$meta\" ]; then nickel export --format json --import-path . $meta 2>/dev/null | jq -r --arg ts \"$name\" '.requires[]? | select(.kind == \"Required\") | .capability' | while read cap; do if echo \"$provides\" | grep -qx \"$cap\"; then echo \"RESOLVED: $ts requires $cap\"; else echo \"UNRESOLVED: $ts requires $cap (Required — no provider found)\"; fi; done; fi; done",
|
|
"depends_on": [
|
|
{
|
|
"kind": "OnSuccess",
|
|
"step": "build_provides_index"
|
|
}
|
|
],
|
|
"id": "resolve_required_capabilities",
|
|
"note": "For every Required capability in requires[], check that at least one other taskserv provides it. UNRESOLVED lines are hard errors — the formula will fail at runtime.",
|
|
"on_error": {
|
|
"backoff_s": 5,
|
|
"max": 3,
|
|
"strategy": "Continue"
|
|
}
|
|
},
|
|
{
|
|
"action": "check_conflictswith_pairs",
|
|
"actor": "Agent",
|
|
"cmd": "all_names=$(for ts in catalog/taskservs/*/; do basename $ts; done); for ts in catalog/taskservs/*/; do name=$(basename $ts); meta=$ts/metadata.ncl; if [ -f \"$meta\" ]; then nickel export --format json --import-path . $meta 2>/dev/null | jq -r --arg ts \"$name\" '.conflicts_with[]?' | while read conflicting; do if echo \"$all_names\" | grep -qx \"$conflicting\"; then echo \"CONFLICT_PAIR: $name conflicts_with $conflicting (both present in registry)\"; fi; done; fi; done | sort -u",
|
|
"depends_on": [
|
|
{
|
|
"kind": "OnSuccess",
|
|
"step": "build_provides_index"
|
|
}
|
|
],
|
|
"id": "detect_conflict_pairs",
|
|
"note": "Detect declared ConflictsWith pairs where both extensions exist in the registry. These pairs must not appear together in the same formula. Emit as audit info — enforcement happens in provisioning-validate-formula.",
|
|
"on_error": {
|
|
"backoff_s": 5,
|
|
"max": 3,
|
|
"strategy": "Continue"
|
|
}
|
|
},
|
|
{
|
|
"action": "emit_integrity_summary",
|
|
"actor": "Agent",
|
|
"cmd": "echo '--- Extension Capability DAG Integrity Report ---' && echo '' && echo '== Capability Provides Index ==' && for ts in catalog/taskservs/*/; do meta=$ts/metadata.ncl; if [ -f \"$meta\" ]; then caps=$(nickel export --format json --import-path . $meta 2>/dev/null | jq -r '[.provides[]?.id] | join(\", \")' 2>/dev/null); reqs=$(nickel export --format json --import-path . $meta 2>/dev/null | jq -r '[.requires[]? | \"\\(.capability)[\\(.kind)]\"] | join(\", \")' 2>/dev/null); echo \" $(basename $ts): provides=[$caps] requires=[$reqs]\"; fi; done && echo '' && echo 'Run with --verbose to show RESOLVED/UNRESOLVED resolution details.'",
|
|
"depends_on": [
|
|
{
|
|
"kind": "Always",
|
|
"step": "resolve_required_capabilities"
|
|
},
|
|
{
|
|
"kind": "Always",
|
|
"step": "detect_conflict_pairs"
|
|
}
|
|
],
|
|
"id": "integrity_report",
|
|
"note": "Print the full capability coverage table showing what each taskserv provides and requires, then a resolution summary.",
|
|
"on_error": {
|
|
"backoff_s": 5,
|
|
"max": 3,
|
|
"strategy": "Stop"
|
|
}
|
|
}
|
|
],
|
|
"strategy": "Override",
|
|
"trigger": "Audit extension capability DAG for completeness, resolution coverage, and conflict safety"
|
|
}
|