diff --git a/.agents/skills/README.md b/.agents/skills/README.md index 3dcc2d7..34f9355 100644 --- a/.agents/skills/README.md +++ b/.agents/skills/README.md @@ -10,12 +10,12 @@ then propagate from the canonical source. Installed skills: -- `msdmd/` +- `canon/` +- `interdependent-work-graph/` - `meta-module-build/` -- `test-build/` -- `risk-boundary-build/` +- `msdmd/` - `ratios/` -- `the-interdependency/` -- `interdependent-work-graph/` -- `canon/` +- `risk-boundary-build/` - `skill-usage/` +- `test-build/` +- `the-interdependency/` diff --git a/.agents/skills/interdependent-work-graph/PORTFOLIO_PLAN.md b/.agents/skills/interdependent-work-graph/PORTFOLIO_PLAN.md new file mode 100644 index 0000000..4911ac3 --- /dev/null +++ b/.agents/skills/interdependent-work-graph/PORTFOLIO_PLAN.md @@ -0,0 +1,95 @@ +# Interdependent work graph — portfolio plan projection + +This is the machine-reachable portfolio projection for the existing `interdependent-work-graph` skill. + +## Contract + +Each participating repository remains self-contained and authoritative for its own canon, implementation, evidence, status, and unresolved boundaries. It publishes exactly one repo-owned report at the conventional path: + +```text +docs/work-graphs/repository-plan-report.json +``` + +The report validates against: + +```text +interdependent-work-graph/repository-plan-report.schema.json +schema: the-interdependency.repository-plan-report +version: 1.0.0 +blob SHA: 9b347b2dff7692054b571602f30ee6d00c2e7265 +``` + +The Git blob SHA pins the exact schema bytes independently of branch names or eventual PR merge strategy. The report's cited `source.commit` separately identifies the repository state being described. The commit which adds or refreshes a report is coordination metadata and does not silently become mathematical, semantic, empirical, measurement, runtime, or theorem evidence. + +## Authority rule + +```text +repo owns claim + evidence + status +skill-lib owns reporting contract + deterministic projection +portfolio plan owns no repo canon +``` + +Aggregation never transfers authority. It never upgrades candidate status, proof status, measurement validity, empirical validity, semantic validity, deployment authority, or permissions. + +## Machine use + +From a workspace containing checked-out repository reports: + +```bash +python interdependent-work-graph/portfolio_plan.py \ + ../a0/docs/work-graphs/repository-plan-report.json \ + ../edcm/docs/work-graphs/repository-plan-report.json \ + ../metapat/docs/work-graphs/repository-plan-report.json \ + ../ucns/docs/work-graphs/repository-plan-report.json \ + ../zfae/docs/work-graphs/repository-plan-report.json \ + docs/work-graphs/repository-plan-report.json \ + --output portfolio-plan.json +``` + +The derived output shape is declared at: + +```text +interdependent-work-graph/portfolio-plan.schema.json +schema: the-interdependency.portfolio-plan +version: 1.0.0 +``` + +The aggregator uses only the Python standard library. It validates the frozen contract identity, rejects duplicate repositories and authority transfer, sorts reports by repository identity, content-addresses every input report, and emits: + +- source identities; +- repo authority and portfolio roles; +- delivered surfaces; +- cross-repository dependencies; +- active frontier; +- next actions; +- blocked work; +- `hmmm`; +- `portfolio_plan_sha256` over the deterministic projected body. + +Local checkout paths are deliberately excluded from the projected body and its digest. Identical report content therefore produces identical portfolio identity regardless of where repositories are checked out. + +## Staleness + +A report is stale when the repository state of interest no longer matches `source.commit`. An aggregator may expose that mismatch if its execution environment can resolve repository HEAD, but it must not rewrite the report or infer what changed. Refreshing a report is a repo-owned operation. + +## Missing reports + +A portfolio view is complete only for reports actually supplied. A missing repository must not be synthesized from memory, neighboring repositories, package metadata, or a consumer's assumptions. If a known participant lacks a report, record that absence as `hmmm` in the calling workflow. + +## Relationship to stack manifests + +`repository-plan-report.json` answers: + +> What part of the larger effort does this repository own, what has it delivered, and what remains live? + +A stack manifest answers: + +> Which exact participants and authority boundaries constitute this particular cross-repository work graph? + +They are complementary. Neither replaces the other. + +## hmmm + +- Automatic discovery of organization repositories is deliberately not part of v1; an explicit input set avoids silently treating repository visibility as portfolio membership. +- Cryptographic producer authentication remains separate from content identity. +- A future service may fetch reports directly from GitHub or another registry, but the deterministic local projection remains the reference behavior. diff --git a/.agents/skills/interdependent-work-graph/portfolio-plan.schema.json b/.agents/skills/interdependent-work-graph/portfolio-plan.schema.json new file mode 100644 index 0000000..8ca7437 --- /dev/null +++ b/.agents/skills/interdependent-work-graph/portfolio-plan.schema.json @@ -0,0 +1,109 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/The-Interdependency/skill-lib/interdependent-work-graph/portfolio-plan.schema.json", + "title": "The Interdependency portfolio plan", + "type": "object", + "additionalProperties": false, + "required": [ + "schema", + "version", + "contract", + "generated_from", + "repositories", + "cross_repository_dependencies", + "active_frontier", + "next_actions", + "blocked", + "hmmm", + "portfolio_plan_sha256" + ], + "properties": { + "schema": {"const": "the-interdependency.portfolio-plan"}, + "version": {"const": "1.0.0"}, + "contract": { + "type": "object", + "additionalProperties": false, + "required": ["repository", "report_schema_path", "report_schema_version", "report_schema_blob_sha"], + "properties": { + "repository": {"const": "The-Interdependency/skill-lib"}, + "report_schema_path": {"const": "interdependent-work-graph/repository-plan-report.schema.json"}, + "report_schema_version": {"const": "1.0.0"}, + "report_schema_blob_sha": {"const": "9b347b2dff7692054b571602f30ee6d00c2e7265"} + } + }, + "generated_from": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["repository", "source_commit", "report_sha256"], + "properties": { + "repository": {"type": "string", "pattern": "^[^/]+/[^/]+$"}, + "source_commit": {"type": "string", "pattern": "^[0-9a-f]{40}$"}, + "report_sha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"} + } + } + }, + "repositories": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["repository", "authority", "portfolio_role", "status", "delivered", "machine_entrypoints"], + "properties": { + "repository": {"type": "string", "pattern": "^[^/]+/[^/]+$"}, + "authority": {"type": "object"}, + "portfolio_role": {"type": "object"}, + "status": {"type": "object"}, + "delivered": {"type": "array"}, + "machine_entrypoints": {"type": "object"} + } + } + }, + "cross_repository_dependencies": { + "type": "array", + "items": { + "type": "object", + "required": ["from", "to", "relation", "authority_transfer"], + "properties": { + "from": {"type": "string", "pattern": "^[^/]+/[^/]+$"}, + "to": {"type": "string", "pattern": "^[^/]+/[^/]+$"}, + "relation": {"type": "string", "minLength": 1}, + "authority_transfer": {"const": false} + } + } + }, + "active_frontier": {"$ref": "#/$defs/repoItems"}, + "blocked": {"$ref": "#/$defs/repoItems"}, + "hmmm": {"$ref": "#/$defs/repoItems"}, + "next_actions": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["repository", "action", "owner", "dependency"], + "properties": { + "repository": {"type": "string", "pattern": "^[^/]+/[^/]+$"}, + "action": {"type": "string", "minLength": 1}, + "owner": {"type": "string", "minLength": 1}, + "dependency": {"type": "string"} + } + } + }, + "portfolio_plan_sha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"} + }, + "$defs": { + "repoItems": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["repository", "item"], + "properties": { + "repository": {"type": "string", "pattern": "^[^/]+/[^/]+$"}, + "item": {"type": "string", "minLength": 1} + } + } + } + } +} diff --git a/.agents/skills/interdependent-work-graph/portfolio_plan.py b/.agents/skills/interdependent-work-graph/portfolio_plan.py new file mode 100644 index 0000000..97b8b54 --- /dev/null +++ b/.agents/skills/interdependent-work-graph/portfolio_plan.py @@ -0,0 +1,228 @@ +# ratios: loc_comments=hmmm imports_exports=hmmm calls_definitions=hmmm +"""Validate repo-owned plan reports and derive one deterministic portfolio plan. + +No network access and no third-party packages are required. Repository reports +remain authoritative for their own claims; this program only validates, +orders, hashes, and projects them into a cross-repository view. + +Usage guidance: + python interdependent-work-graph/portfolio_plan.py \ + ../a0/docs/work-graphs/repository-plan-report.json \ + ../edcm/docs/work-graphs/repository-plan-report.json \ + --output portfolio-plan.json + +Supply only the repositories intentionally included in the portfolio view. +Missing repositories are not auto-discovered or synthesized. A report must pin +the exact frozen report-schema blob and the source commit it describes. +""" + +# === MODULE_BUILD === +# id: interdependent_work_graph_portfolio_plan +# module_name: portfolio_plan +# module_kind: instrument +# summary: validates repo-owned plan reports and derives a deterministic cross-repository portfolio projection without transferring authority +# owner: The-Interdependency/skill-lib maintainers +# public_surface: load_report, build_portfolio, main +# internal_surface: validate_report, canonical_bytes, digest +# auth_boundary: none +# storage_boundary: none +# network_boundary: none +# user_data_boundary: none +# admin_only: false +# tests: tests/test_interdependent_work_graph_portfolio_plan.py +# rollout: explicit CLI or library invocation after repo reports are supplied +# rollback: remove the aggregator, schemas, companion docs, and portfolio projection section without changing repo-owned source claims +# unresolved: automatic portfolio membership discovery, persistent live service, cryptographic producer authentication +# === END MODULE_BUILD === + +from __future__ import annotations + +import argparse +import hashlib +import json +import re +from pathlib import Path +from typing import Any + +REPORT_SCHEMA = "the-interdependency.repository-plan-report" +REPORT_VERSION = "1.0.0" +PLAN_SCHEMA = "the-interdependency.portfolio-plan" +PLAN_VERSION = "1.0.0" +CONTRACT_REPOSITORY = "The-Interdependency/skill-lib" +CONTRACT_PATH = "interdependent-work-graph/repository-plan-report.schema.json" +CONTRACT_BLOB_SHA = "9b347b2dff7692054b571602f30ee6d00c2e7265" +COMMIT_RE = re.compile(r"^[0-9a-f]{40}$") + + +def canonical_bytes(value: Any) -> bytes: + return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8") + + +def digest(value: Any) -> str: + return hashlib.sha256(canonical_bytes(value)).hexdigest() + + +def _require(condition: bool, message: str) -> None: + if not condition: + raise ValueError(message) + + +def _string_list(value: Any, field: str) -> list[str]: + _require(isinstance(value, list), f"{field} must be an array") + _require(all(isinstance(item, str) and item for item in value), f"{field} must contain non-empty strings") + return value + + +def validate_report(report: dict[str, Any], source_path: Path) -> None: + _require(report.get("schema") == REPORT_SCHEMA, f"{source_path}: unsupported schema") + _require(report.get("version") == REPORT_VERSION, f"{source_path}: unsupported version") + repository = report.get("repository") + _require(isinstance(repository, str) and repository.count("/") == 1, f"{source_path}: invalid repository") + + contract = report.get("contract") + _require(isinstance(contract, dict), f"{source_path}: contract must be an object") + _require(contract.get("repository") == CONTRACT_REPOSITORY, f"{source_path}: wrong contract repository") + _require(contract.get("path") == CONTRACT_PATH, f"{source_path}: wrong contract path") + _require(contract.get("version") == REPORT_VERSION, f"{source_path}: wrong contract version") + _require(contract.get("blob_sha") == CONTRACT_BLOB_SHA, f"{source_path}: report is not pinned to the frozen contract blob") + + source = report.get("source") + _require(isinstance(source, dict), f"{source_path}: source must be an object") + _require(COMMIT_RE.fullmatch(str(source.get("commit", ""))) is not None, f"{source_path}: source.commit must be 40 lowercase hex characters") + for field in ("branch", "generated_at", "note"): + _require(isinstance(source.get(field), str) and source[field], f"{source_path}: source.{field} is required") + + authority = report.get("authority") + _require(isinstance(authority, dict), f"{source_path}: authority must be an object") + _require(bool(_string_list(authority.get("owns"), "authority.owns")), f"{source_path}: authority.owns may not be empty") + _string_list(authority.get("does_not_own"), "authority.does_not_own") + _require(bool(_string_list(authority.get("non_transfer"), "authority.non_transfer")), f"{source_path}: authority.non_transfer may not be empty") + + portfolio_role = report.get("portfolio_role") + _require(isinstance(portfolio_role, dict), f"{source_path}: portfolio_role must be an object") + _require(isinstance(portfolio_role.get("summary"), str) and portfolio_role["summary"], f"{source_path}: portfolio_role.summary is required") + reports_to = portfolio_role.get("reports_to") + _require(isinstance(reports_to, dict), f"{source_path}: portfolio_role.reports_to must be an object") + _require(reports_to.get("repository") == CONTRACT_REPOSITORY, f"{source_path}: reports_to.repository must be skill-lib") + _require(reports_to.get("skill") == "interdependent-work-graph", f"{source_path}: reports_to.skill must be interdependent-work-graph") + _require(isinstance(reports_to.get("relation"), str) and reports_to["relation"], f"{source_path}: reports_to.relation is required") + + status = report.get("status") + _require(isinstance(status, dict), f"{source_path}: status must be an object") + for field in ("state", "current_claim"): + _require(isinstance(status.get(field), str) and status[field], f"{source_path}: status.{field} is required") + + delivered = report.get("delivered") + _require(isinstance(delivered, list), f"{source_path}: delivered must be an array") + for index, item in enumerate(delivered): + _require(isinstance(item, dict), f"{source_path}: delivered[{index}] must be an object") + for field in ("surface", "status", "boundary"): + _require(isinstance(item.get(field), str) and item[field], f"{source_path}: delivered[{index}].{field} is required") + + _string_list(report.get("active_frontier"), "active_frontier") + _string_list(report.get("blocked"), "blocked") + _string_list(report.get("hmmm"), "hmmm") + + actions = report.get("next_actions") + _require(isinstance(actions, list), f"{source_path}: next_actions must be an array") + for index, action in enumerate(actions): + _require(isinstance(action, dict), f"{source_path}: next_actions[{index}] must be an object") + for field in ("action", "owner", "dependency"): + _require(isinstance(action.get(field), str), f"{source_path}: next_actions[{index}].{field} must be a string") + _require(bool(action["action"] and action["owner"]), f"{source_path}: next_actions[{index}] requires action and owner") + + relations = report.get("cross_repository_relations") + _require(isinstance(relations, list), f"{source_path}: cross_repository_relations must be an array") + for index, relation in enumerate(relations): + _require(isinstance(relation, dict), f"{source_path}: cross_repository_relations[{index}] must be an object") + _require(isinstance(relation.get("repository"), str) and relation["repository"].count("/") == 1, f"{source_path}: invalid relation repository") + _require(isinstance(relation.get("relation"), str) and relation["relation"], f"{source_path}: relation text is required") + _require(relation.get("authority_transfer") is False, f"{source_path}: authority transfer must be false") + + entrypoints = report.get("machine_entrypoints") + _require(isinstance(entrypoints, dict) and entrypoints, f"{source_path}: machine_entrypoints must be a non-empty object") + _require(all(isinstance(k, str) and k and isinstance(v, str) and v for k, v in entrypoints.items()), f"{source_path}: machine_entrypoints keys and values must be non-empty strings") + + +def load_report(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + _require(isinstance(value, dict), f"{path}: report root must be an object") + validate_report(value, path) + return value + + +def build_portfolio(reports_with_paths: list[tuple[Path, dict[str, Any]]]) -> dict[str, Any]: + ordered = sorted(reports_with_paths, key=lambda item: item[1]["repository"]) + repository_names = [report["repository"] for _, report in ordered] + _require(len(repository_names) == len(set(repository_names)), "duplicate repository reports are not allowed") + + generated_from: list[dict[str, Any]] = [] + repository_views: list[dict[str, Any]] = [] + relations: list[dict[str, Any]] = [] + active_frontier: list[dict[str, str]] = [] + next_actions: list[dict[str, Any]] = [] + blocked: list[dict[str, str]] = [] + hmmm: list[dict[str, str]] = [] + + for _, report in ordered: + repository = report["repository"] + generated_from.append({ + "repository": repository, + "source_commit": report["source"]["commit"], + "report_sha256": digest(report), + }) + repository_views.append({ + "repository": repository, + "authority": report["authority"], + "portfolio_role": report["portfolio_role"], + "status": report["status"], + "delivered": report["delivered"], + "machine_entrypoints": report["machine_entrypoints"], + }) + for relation in report["cross_repository_relations"]: + relations.append({"from": repository, "to": relation["repository"], **{k: v for k, v in relation.items() if k != "repository"}}) + active_frontier.extend({"repository": repository, "item": item} for item in report["active_frontier"]) + next_actions.extend({"repository": repository, **action} for action in report["next_actions"]) + blocked.extend({"repository": repository, "item": item} for item in report["blocked"]) + hmmm.extend({"repository": repository, "item": item} for item in report["hmmm"]) + + body = { + "schema": PLAN_SCHEMA, + "version": PLAN_VERSION, + "contract": { + "repository": CONTRACT_REPOSITORY, + "report_schema_path": CONTRACT_PATH, + "report_schema_version": REPORT_VERSION, + "report_schema_blob_sha": CONTRACT_BLOB_SHA, + }, + "generated_from": generated_from, + "repositories": repository_views, + "cross_repository_dependencies": sorted(relations, key=lambda item: (item["from"], item["to"], item["relation"])), + "active_frontier": active_frontier, + "next_actions": next_actions, + "blocked": blocked, + "hmmm": hmmm, + } + return {**body, "portfolio_plan_sha256": digest(body)} + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("reports", nargs="+", type=Path, help="repo-owned repository-plan-report.json files") + parser.add_argument("--output", type=Path, help="write JSON to this path instead of stdout") + args = parser.parse_args() + + reports = [(path, load_report(path)) for path in args.reports] + portfolio = build_portfolio(reports) + rendered = json.dumps(portfolio, indent=2, sort_keys=True, ensure_ascii=False) + "\n" + if args.output: + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text(rendered, encoding="utf-8") + else: + print(rendered, end="") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) +# ratios: loc_comments=hmmm imports_exports=hmmm calls_definitions=hmmm diff --git a/.agents/skills/interdependent-work-graph/repository-plan-report.schema.json b/.agents/skills/interdependent-work-graph/repository-plan-report.schema.json new file mode 100644 index 0000000..9b347b2 --- /dev/null +++ b/.agents/skills/interdependent-work-graph/repository-plan-report.schema.json @@ -0,0 +1,137 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/The-Interdependency/skill-lib/interdependent-work-graph/repository-plan-report.schema.json", + "title": "The Interdependency repository plan report", + "type": "object", + "additionalProperties": false, + "required": [ + "schema", + "version", + "repository", + "contract", + "source", + "authority", + "portfolio_role", + "status", + "delivered", + "active_frontier", + "next_actions", + "blocked", + "cross_repository_relations", + "machine_entrypoints", + "hmmm" + ], + "properties": { + "schema": {"const": "the-interdependency.repository-plan-report"}, + "version": {"const": "1.0.0"}, + "repository": {"type": "string", "pattern": "^[^/]+/[^/]+$"}, + "contract": { + "type": "object", + "additionalProperties": false, + "required": ["repository", "path", "version", "blob_sha"], + "properties": { + "repository": {"const": "The-Interdependency/skill-lib"}, + "path": {"const": "interdependent-work-graph/repository-plan-report.schema.json"}, + "version": {"const": "1.0.0"}, + "blob_sha": {"type": "string", "pattern": "^[0-9a-f]{40}$"} + } + }, + "source": { + "type": "object", + "additionalProperties": false, + "required": ["branch", "commit", "generated_at", "note"], + "properties": { + "branch": {"type": "string", "minLength": 1}, + "commit": {"type": "string", "pattern": "^[0-9a-f]{40}$"}, + "generated_at": {"type": "string", "format": "date"}, + "note": {"type": "string", "minLength": 1} + } + }, + "authority": { + "type": "object", + "additionalProperties": false, + "required": ["owns", "does_not_own", "non_transfer"], + "properties": { + "owns": {"type": "array", "items": {"type": "string", "minLength": 1}, "minItems": 1}, + "does_not_own": {"type": "array", "items": {"type": "string", "minLength": 1}}, + "non_transfer": {"type": "array", "items": {"type": "string", "minLength": 1}, "minItems": 1} + } + }, + "portfolio_role": { + "type": "object", + "additionalProperties": false, + "required": ["summary", "reports_to"], + "properties": { + "summary": {"type": "string", "minLength": 1}, + "upstream_contract": {"type": "string"}, + "downstream_contract": {"type": "string"}, + "reports_to": { + "type": "object", + "additionalProperties": false, + "required": ["repository", "skill", "relation"], + "properties": { + "repository": {"const": "The-Interdependency/skill-lib"}, + "skill": {"const": "interdependent-work-graph"}, + "relation": {"type": "string", "minLength": 1} + } + } + } + }, + "status": { + "type": "object", + "additionalProperties": true, + "required": ["state", "current_claim"], + "properties": { + "state": {"type": "string", "minLength": 1}, + "current_claim": {"type": "string", "minLength": 1} + } + }, + "delivered": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["surface", "status", "boundary"], + "properties": { + "surface": {"type": "string", "minLength": 1}, + "status": {"type": "string", "minLength": 1}, + "boundary": {"type": "string", "minLength": 1} + } + } + }, + "active_frontier": {"type": "array", "items": {"type": "string", "minLength": 1}}, + "next_actions": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["action", "owner", "dependency"], + "properties": { + "action": {"type": "string", "minLength": 1}, + "owner": {"type": "string", "minLength": 1}, + "dependency": {"type": "string"} + } + } + }, + "blocked": {"type": "array", "items": {"type": "string", "minLength": 1}}, + "cross_repository_relations": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true, + "required": ["repository", "relation", "authority_transfer"], + "properties": { + "repository": {"type": "string", "pattern": "^[^/]+/[^/]+$"}, + "relation": {"type": "string", "minLength": 1}, + "authority_transfer": {"const": false} + } + } + }, + "machine_entrypoints": { + "type": "object", + "minProperties": 1, + "additionalProperties": {"type": "string", "minLength": 1} + }, + "hmmm": {"type": "array", "items": {"type": "string", "minLength": 1}} + } +} diff --git a/ptcna_msdmd.ts b/ptcna_msdmd.ts index b2fb3f5..5691aa3 100644 --- a/ptcna_msdmd.ts +++ b/ptcna_msdmd.ts @@ -2,6 +2,28 @@ import { defineMsdmdCollection } from "./.agents/skills/msdmd/collection"; export default defineMsdmdCollection({ "declarations": [ + { + "block": "MODULE_BUILD", + "fields": { + "admin_only": "false", + "auth_boundary": "none", + "internal_surface": "validate_report, canonical_bytes, digest", + "module_kind": "instrument", + "module_name": "portfolio_plan", + "network_boundary": "none", + "owner": "The-Interdependency/skill-lib maintainers", + "public_surface": "load_report, build_portfolio, main", + "rollback": "remove the aggregator, schemas, companion docs, and portfolio projection section without changing repo-owned source claims", + "rollout": "explicit CLI or library invocation after repo reports are supplied", + "storage_boundary": "none", + "summary": "validates repo-owned plan reports and derives a deterministic cross-repository portfolio projection without transferring authority", + "tests": "tests/test_interdependent_work_graph_portfolio_plan.py", + "unresolved": "automatic portfolio membership discovery, persistent live service, cryptographic producer authentication", + "user_data_boundary": "none" + }, + "file": ".agents/skills/interdependent-work-graph/portfolio_plan.py", + "id": "interdependent_work_graph_portfolio_plan" + }, { "block": "BOUNDARIES", "fields": { @@ -2684,6 +2706,13 @@ export default defineMsdmdCollection({ "source_id": "check_zeta_suspends_without_provider", "to": "python3" }, + { + "from": "interdependent_work_graph_portfolio_plan", + "kind": "owns", + "source_block": "MODULE_BUILD", + "source_id": "interdependent_work_graph_portfolio_plan", + "to": "The-Interdependency/skill-lib maintainers" + }, { "from": "pcna_helix_vis", "kind": "owns",