From 6509783c45f3cb874edbec0fa28fcfc98a1fc11b Mon Sep 17 00:00:00 2001 From: saagpatel Date: Tue, 4 Aug 2026 22:03:59 -0700 Subject: [PATCH 1/2] feat(portfolio): add reusable consumer contract profiles --- .../operator-control-plane-v1/manifest.json | 70 + .../portfolio-truth.json | 1661 +++++++++++++++++ .../public-site-projection-v1/manifest.json | 90 + ...nerate_portfolio_truth_contract_fixture.py | 17 + src/portfolio_truth_contract_fixture.py | 160 +- src/portfolio_truth_validate.py | 12 +- .../test_portfolio_truth_contract_fixture.py | 79 + 7 files changed, 2084 insertions(+), 5 deletions(-) create mode 100644 fixtures/contracts/operator-control-plane-v1/manifest.json create mode 100644 fixtures/contracts/portable-consumers-v1/portfolio-truth.json create mode 100644 fixtures/contracts/public-site-projection-v1/manifest.json diff --git a/fixtures/contracts/operator-control-plane-v1/manifest.json b/fixtures/contracts/operator-control-plane-v1/manifest.json new file mode 100644 index 00000000..e19014b7 --- /dev/null +++ b/fixtures/contracts/operator-control-plane-v1/manifest.json @@ -0,0 +1,70 @@ +{ + "acceptance": { + "coverage_cases": [ + { + "case_id": "valid-current-schema", + "expected": "accept", + "source": "artifact" + }, + { + "case_id": "additive-canary", + "expected": "accept-ignore-addition", + "pointer": "/projects/0/additive_contract_canary", + "source": "artifact" + }, + { + "case_id": "missing-schema-version", + "expected": "reject", + "mutation": { + "op": "remove", + "path": "/schema_version" + }, + "source": "artifact" + }, + { + "case_id": "malformed-root", + "expected": "reject", + "source": "literal", + "value": [] + }, + { + "case_id": "contradictory-contract-envelope", + "expected": "reject", + "mutation": { + "op": "add", + "path": "/contract", + "value": { + "compatibility": "additive", + "id": "ghra.portfolio_truth", + "version": "0.12.0" + } + }, + "source": "artifact" + } + ], + "fail_closed_behavior": "incompatible-artifact-yields-unavailable-health" + }, + "consumer_profile": { + "compatibility_policy": "additive-0.x", + "id": "operator-control-plane-v1" + }, + "contract_version": "ghra-portfolio-truth-portable.v1", + "fixture": { + "additive_canary_paths": [ + "contract_fixture", + "projects[0].additive_contract_canary" + ], + "evaluation_time": "2026-08-01T06:00:00+00:00", + "generated_at": "2026-08-01T00:00:00+00:00", + "producer_evidence": "absent", + "project_count": 4 + }, + "portfolio_truth_schema_version": "0.11.0", + "producer": { + "artifact_path": "fixtures/contracts/portable-consumers-v1/portfolio-truth.json", + "artifact_sha256": "8af46309e1b7a891edf7489dcbfa5971cc3453fe983b9a824098d87dc42105d3", + "generator": "src.portfolio_truth_contract_fixture:build_portable_contract_fixture", + "manifest_path": "fixtures/contracts/operator-control-plane-v1/manifest.json", + "repository": "saagpatel/GithubRepoAuditor" + } +} diff --git a/fixtures/contracts/portable-consumers-v1/portfolio-truth.json b/fixtures/contracts/portable-consumers-v1/portfolio-truth.json new file mode 100644 index 00000000..34e30f18 --- /dev/null +++ b/fixtures/contracts/portable-consumers-v1/portfolio-truth.json @@ -0,0 +1,1661 @@ +{ + "contract_fixture": { + "contract_version": "ghra-portfolio-truth-portable.v1", + "deterministic": true, + "producer_evidence": "absent", + "security_evidence_semantics": "synthetic-cross-receipt-state-matrix" + }, + "coverage": [ + { + "project_count": 4, + "source": "workspace", + "state": "observed" + }, + { + "observed_count": 4, + "project_count": 4, + "source": "git", + "state": "observed" + }, + { + "cohort_complete_count": 1, + "cohort_partial_count": 1, + "cohort_repository_count": 3, + "cohort_stale_count": 1, + "cohort_unknown_count": 0, + "complete_repo_count": 1, + "partial_repo_count": 1, + "project_count": 4, + "provider_observed_counts": { + "code_scanning": 1, + "dependabot": 2, + "secret_scanning": 1 + }, + "provider_zero_finding_counts": { + "code_scanning": 1, + "dependabot": 0, + "secret_scanning": 1 + }, + "remote_default_branch_counts": { + "credential_unavailable": 0, + "forbidden": 0, + "malformed": 0, + "not_found": 0, + "not_requested": 0, + "observed": 2, + "partial": 0, + "rate_limited": 0, + "stale": 1, + "transient_error": 0, + "unknown": 1 + }, + "scanned_count": 1, + "source": "github_security", + "stale_count": 1, + "state": "partial", + "unknown_count": 1 + }, + { + "observed_count": 0, + "source": "notion", + "state": "unknown" + } + ], + "derivation_policy_version": "portfolio_attention.v3", + "exclusions": { + "counts": {}, + "policy_version": "workspace_discovery.v3" + }, + "generated_at": "2026-08-01T00:00:00+00:00", + "inputs": { + "catalog": { + "observed_at": "2026-08-01T00:00:00+00:00", + "sha256": null, + "source_id": "portfolio-catalog" + }, + "github_security": { + "age_hours": 0.0, + "cohort_policy": "portfolio-default-attention-v1", + "cohort_repository_count": 3, + "content_sha256": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "path": "/demo-workspace/github-security-coverage.json", + "produced_at": "2026-08-01T00:00:00+00:00", + "producer_commit": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "receipt_id": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "schema_version": "GitHubSecurityCoverageReceiptV1", + "source_id": "github-security-coverage-receipt", + "state": "fresh" + }, + "notion": { + "carried_from_generated_at": null, + "mode": "unavailable", + "observed_at": null + }, + "workspace": { + "observed_at": "2026-08-01T00:00:00+00:00", + "source_id": "projects-root" + } + }, + "precedence_matrix": { + "declared.category": [ + "catalog_repo", + "catalog_group", + "legacy_registry" + ], + "declared.criticality": [ + "catalog_repo", + "catalog_group" + ], + "declared.intended_disposition": [ + "catalog_repo", + "catalog_group" + ], + "declared.lifecycle_state": [ + "catalog_repo", + "catalog_group" + ], + "declared.maturity_program": [ + "catalog_repo", + "catalog_group", + "catalog_defaults" + ], + "declared.notes": [ + "catalog_repo", + "catalog_group", + "legacy_registry" + ], + "declared.operating_path": [ + "normalized" + ], + "declared.owner": [ + "catalog_repo", + "catalog_group" + ], + "declared.purpose": [ + "catalog_repo", + "catalog_group" + ], + "declared.review_cadence": [ + "catalog_repo", + "catalog_group" + ], + "declared.target_maturity": [ + "catalog_repo", + "catalog_group", + "catalog_defaults" + ], + "declared.team": [ + "catalog_repo", + "catalog_group" + ], + "declared.tool_provenance": [ + "catalog_repo", + "catalog_group", + "inference", + "legacy_registry" + ], + "derived.activity_status": [ + "derived" + ], + "derived.archived": [ + "derived" + ], + "derived.attention_state": [ + "derived" + ], + "derived.context_files": [ + "workspace" + ], + "derived.context_quality": [ + "workspace", + "catalog_repo", + "catalog_group" + ], + "derived.current_state_present": [ + "workspace" + ], + "derived.has_ci": [ + "workspace" + ], + "derived.has_tests": [ + "workspace" + ], + "derived.known_risks_present": [ + "workspace" + ], + "derived.last_meaningful_activity_at": [ + "git", + "workspace" + ], + "derived.next_recommended_move_present": [ + "workspace" + ], + "derived.path_confidence": [ + "normalized" + ], + "derived.path_override": [ + "normalized" + ], + "derived.path_rationale": [ + "normalized" + ], + "derived.primary_context_file": [ + "workspace" + ], + "derived.project_summary_present": [ + "workspace" + ], + "derived.readme_char_count": [ + "workspace" + ], + "derived.run_instructions_present": [ + "workspace" + ], + "derived.stack": [ + "workspace", + "legacy_registry" + ], + "derived.stack_present": [ + "workspace" + ] + }, + "producer": {}, + "projects": [ + { + "additive_contract_canary": { + "consumer_behavior": "ignore-compatible-addition" + }, + "advisory": { + "legacy_category": "lab", + "legacy_context_quality": "minimum-viable", + "legacy_status": "dormant", + "legacy_tool_provenance": "", + "notion_current_state": "", + "notion_momentum": "", + "notion_portfolio_call": "" + }, + "declared": { + "automation_eligible": true, + "category": "learning", + "criticality": "medium", + "doctor_standard": "", + "intended_disposition": "", + "lifecycle_state": "dormant", + "maturity_program": "maintain", + "notes": "", + "operating_path": "maintain", + "owner": "demo-operator", + "purpose": "Exploratory prototype kept for reference only.", + "review_cadence": "monthly", + "target_maturity": "operating", + "team": "", + "tool_provenance": "claude-code" + }, + "derived": { + "activity_status": "stale", + "archived": false, + "attention_state": "parked", + "context_file_count": 1, + "context_files": [ + "AGENTS.md" + ], + "context_quality": "minimum-viable", + "current_state_present": true, + "has_ci": true, + "has_license": true, + "has_tests": true, + "known_risks_present": true, + "last_meaningful_activity_at": "2026-01-23T00:00:00+00:00", + "next_recommended_move_present": true, + "path_confidence": "medium", + "path_override": "", + "path_rationale": "Stable path is Maintain from explicit operating path.", + "primary_context_file": "AGENTS.md", + "project_summary_present": true, + "readme_char_count": 1174, + "release_count": 0, + "run_instructions_present": true, + "stack": [ + "Python" + ], + "stack_present": true + }, + "identity": { + "default_branch": "main", + "display_name": "Quartz Signal", + "group_key": "lab", + "group_label": "Experiments Lab", + "has_git": true, + "path": "lab/quartz-signal", + "project_key": "lab/quartz-signal", + "repo_full_name": "demo-org/quartz-signal", + "section_label": "Experiments Lab", + "section_marker": "lab/", + "top_level_dir": "lab" + }, + "provenance": { + "declared.category": { + "detail": "learning", + "source": "demo-fixture" + }, + "declared.criticality": { + "detail": "medium", + "source": "demo-fixture" + }, + "declared.doctor_standard": { + "detail": "", + "source": "demo-fixture" + }, + "declared.intended_disposition": { + "detail": "", + "source": "demo-fixture" + }, + "declared.lifecycle_state": { + "detail": "dormant", + "source": "demo-fixture" + }, + "declared.maturity_program": { + "detail": "maintain", + "source": "demo-fixture" + }, + "declared.notes": { + "detail": "", + "source": "demo-fixture" + }, + "declared.operating_path": { + "detail": "explicit-operating-path", + "source": "normalized" + }, + "declared.owner": { + "detail": "demo-operator", + "source": "demo-fixture" + }, + "declared.purpose": { + "detail": "Exploratory prototype kept for reference only.", + "source": "demo-fixture" + }, + "declared.review_cadence": { + "detail": "monthly", + "source": "demo-fixture" + }, + "declared.target_maturity": { + "detail": "operating", + "source": "demo-fixture" + }, + "declared.team": { + "detail": "", + "source": "demo-fixture" + }, + "declared.tool_provenance": { + "detail": "claude-code", + "source": "demo-fixture" + }, + "derived.activity_status": { + "detail": "stale", + "source": "demo-fixture" + }, + "derived.archived": { + "detail": "false", + "source": "demo-fixture" + }, + "derived.attention_state": { + "detail": "parked", + "source": "demo-fixture" + }, + "derived.context_files": { + "detail": "1", + "source": "demo-fixture" + }, + "derived.context_quality": { + "detail": "minimum-viable", + "source": "demo-fixture" + }, + "derived.current_state_present": { + "detail": "true", + "source": "demo-fixture" + }, + "derived.known_risks_present": { + "detail": "true", + "source": "demo-fixture" + }, + "derived.last_meaningful_activity_at": { + "detail": "2026-01-23T00:00:00+00:00", + "source": "demo-fixture" + }, + "derived.next_recommended_move_present": { + "detail": "true", + "source": "demo-fixture" + }, + "derived.path_confidence": { + "detail": "medium", + "source": "normalized" + }, + "derived.path_override": { + "detail": "", + "source": "normalized" + }, + "derived.path_rationale": { + "detail": "Stable path is Maintain from explicit operating path.", + "source": "normalized" + }, + "derived.primary_context_file": { + "detail": "AGENTS.md", + "source": "demo-fixture" + }, + "derived.project_summary_present": { + "detail": "true", + "source": "demo-fixture" + }, + "derived.run_instructions_present": { + "detail": "true", + "source": "demo-fixture" + }, + "derived.stack": { + "detail": "Python", + "source": "demo-fixture" + }, + "derived.stack_present": { + "detail": "true", + "source": "demo-fixture" + }, + "risk.doctor_gap": { + "detail": "false", + "source": "derived" + }, + "risk.risk_tier": { + "detail": "baseline", + "source": "derived" + } + }, + "repository_state": { + "local": { + "ahead": 0, + "behind": 0, + "branch": "main", + "dirty": false, + "dirty_path_count": 0, + "head": "f0880b359ae6df38b4ea8eae386c6eca43085e5cfe95ccb0814289e3427cbac2", + "path": "/demo-workspace/lab/quartz-signal", + "upstream": "origin/main", + "upstream_branch": "main", + "upstream_observation_source": "local_tracking_ref", + "upstream_remote": "origin" + }, + "observed_at": "2026-08-01T00:00:00+00:00", + "remote_default_branch": { + "archived": null, + "default_branch": null, + "head_sha": null, + "observed_at": "2026-07-30T23:00:00+00:00", + "reason": "receipt_stale", + "reason_code": "stale_observation", + "source": "github-graphql-default-branch-head-v1", + "state": "stale" + }, + "state": "observed", + "topology": { + "configured_path": "/demo-workspace/lab/quartz-signal", + "kind": "working_repository", + "linked_worktree_count": 0, + "selection": { + "candidate_count": 0, + "reason": "independent remote-default evidence is not observed (state=stale)", + "reason_code": "remote_default_branch_unavailable", + "source": "github-graphql-default-branch-head-v1", + "state": "unknown" + }, + "worktree_count": 1 + }, + "worktrees": [ + { + "ahead": 0, + "bare": false, + "behind": 0, + "branch": "main", + "detached": false, + "dirty": false, + "dirty_path_count": 0, + "head": "f0880b359ae6df38b4ea8eae386c6eca43085e5cfe95ccb0814289e3427cbac2", + "path": "/demo-workspace/lab/quartz-signal", + "state": "observed", + "upstream": "origin/main", + "upstream_branch": "main", + "upstream_observation_source": "local_tracking_ref", + "upstream_remote": "origin" + } + ] + }, + "risk": { + "context_risk": false, + "doctor_gap": false, + "path_risk": false, + "risk_factors": [], + "risk_summary": "No non-security risk factors detected; GitHub security coverage is stale.", + "risk_tier": "baseline", + "security_risk": false + }, + "security": { + "alerts_available": false, + "code_scanning_critical": null, + "code_scanning_high": null, + "cohort_member": true, + "cohort_policy": "portfolio-default-attention-v1", + "coverage_state": "stale", + "dependabot_critical": null, + "dependabot_high": null, + "dependabot_low": null, + "dependabot_medium": null, + "open_high_critical": 0, + "providers": { + "code_scanning": { + "completed": false, + "conditional": { + "requested": true, + "result": "modified" + }, + "counts": null, + "etag": null, + "http_classification": "success", + "http_status": 200, + "last_modified": null, + "observed_at": "2026-07-30T23:00:00+00:00", + "pagination_complete": true, + "reason": "receipt_stale", + "reason_code": "stale_observation", + "state": "stale", + "zero_findings": null + }, + "dependabot": { + "completed": false, + "conditional": { + "requested": true, + "result": "modified" + }, + "counts": null, + "etag": null, + "http_classification": "success", + "http_status": 200, + "last_modified": null, + "observed_at": "2026-07-30T23:00:00+00:00", + "pagination_complete": true, + "reason": "receipt_stale", + "reason_code": "stale_observation", + "state": "stale", + "zero_findings": null + }, + "secret_scanning": { + "completed": false, + "conditional": { + "requested": true, + "result": "modified" + }, + "counts": null, + "etag": null, + "http_classification": "success", + "http_status": 200, + "last_modified": null, + "observed_at": "2026-07-30T23:00:00+00:00", + "pagination_complete": true, + "reason": "receipt_stale", + "reason_code": "stale_observation", + "state": "stale", + "zero_findings": null + } + }, + "receipt_schema_version": "GitHubSecurityCoverageReceiptV1", + "receipt_state": "stale", + "secret_scanning_open": null, + "source_produced_at": "2026-07-30T23:00:00+00:00" + }, + "warnings": [] + }, + { + "advisory": { + "legacy_category": "lab", + "legacy_context_quality": "boilerplate", + "legacy_status": "manual-only", + "legacy_tool_provenance": "", + "notion_current_state": "", + "notion_momentum": "", + "notion_portfolio_call": "" + }, + "declared": { + "automation_eligible": true, + "category": "learning", + "criticality": "medium", + "doctor_standard": "", + "intended_disposition": "", + "lifecycle_state": "manual-only", + "maturity_program": "maintain", + "notes": "", + "operating_path": "maintain", + "owner": "demo-operator", + "purpose": "Exploratory prototype kept for reference only.", + "review_cadence": "monthly", + "target_maturity": "operating", + "team": "", + "tool_provenance": "codex" + }, + "derived": { + "activity_status": "recent", + "archived": false, + "attention_state": "manual-only", + "context_file_count": 1, + "context_files": [ + "AGENTS.md" + ], + "context_quality": "boilerplate", + "current_state_present": false, + "has_ci": true, + "has_license": true, + "has_tests": false, + "known_risks_present": false, + "last_meaningful_activity_at": "2026-07-11T00:00:00+00:00", + "next_recommended_move_present": false, + "path_confidence": "low", + "path_override": "investigate", + "path_rationale": "Stable path is Maintain from explicit operating path. Context quality is still too weak for path guidance to stand on its own. Treat this repo as investigate until path confidence improves.", + "primary_context_file": "AGENTS.md", + "project_summary_present": true, + "readme_char_count": 1311, + "release_count": 0, + "run_instructions_present": false, + "stack": [ + "C" + ], + "stack_present": false + }, + "identity": { + "default_branch": "main", + "display_name": "Solstice Cairn", + "group_key": "lab", + "group_label": "Experiments Lab", + "has_git": true, + "path": "lab/solstice-cairn", + "project_key": "lab/solstice-cairn", + "repo_full_name": "demo-org/solstice-cairn", + "section_label": "Experiments Lab", + "section_marker": "lab/", + "top_level_dir": "lab" + }, + "provenance": { + "declared.category": { + "detail": "learning", + "source": "demo-fixture" + }, + "declared.criticality": { + "detail": "medium", + "source": "demo-fixture" + }, + "declared.doctor_standard": { + "detail": "", + "source": "demo-fixture" + }, + "declared.intended_disposition": { + "detail": "", + "source": "demo-fixture" + }, + "declared.lifecycle_state": { + "detail": "manual-only", + "source": "demo-fixture" + }, + "declared.maturity_program": { + "detail": "maintain", + "source": "demo-fixture" + }, + "declared.notes": { + "detail": "", + "source": "demo-fixture" + }, + "declared.operating_path": { + "detail": "explicit-operating-path", + "source": "normalized" + }, + "declared.owner": { + "detail": "demo-operator", + "source": "demo-fixture" + }, + "declared.purpose": { + "detail": "Exploratory prototype kept for reference only.", + "source": "demo-fixture" + }, + "declared.review_cadence": { + "detail": "monthly", + "source": "demo-fixture" + }, + "declared.target_maturity": { + "detail": "operating", + "source": "demo-fixture" + }, + "declared.team": { + "detail": "", + "source": "demo-fixture" + }, + "declared.tool_provenance": { + "detail": "codex", + "source": "demo-fixture" + }, + "derived.activity_status": { + "detail": "recent", + "source": "demo-fixture" + }, + "derived.archived": { + "detail": "false", + "source": "demo-fixture" + }, + "derived.attention_state": { + "detail": "manual-only", + "source": "demo-fixture" + }, + "derived.context_files": { + "detail": "1", + "source": "demo-fixture" + }, + "derived.context_quality": { + "detail": "boilerplate", + "source": "demo-fixture" + }, + "derived.current_state_present": { + "detail": "false", + "source": "demo-fixture" + }, + "derived.known_risks_present": { + "detail": "false", + "source": "demo-fixture" + }, + "derived.last_meaningful_activity_at": { + "detail": "2026-07-11T00:00:00+00:00", + "source": "demo-fixture" + }, + "derived.next_recommended_move_present": { + "detail": "false", + "source": "demo-fixture" + }, + "derived.path_confidence": { + "detail": "low", + "source": "normalized" + }, + "derived.path_override": { + "detail": "investigate", + "source": "normalized" + }, + "derived.path_rationale": { + "detail": "Stable path is Maintain from explicit operating path. Context quality is still too weak for path guidance to stand on its own. Treat this repo as investigate until path confidence improves.", + "source": "normalized" + }, + "derived.primary_context_file": { + "detail": "AGENTS.md", + "source": "demo-fixture" + }, + "derived.project_summary_present": { + "detail": "true", + "source": "demo-fixture" + }, + "derived.run_instructions_present": { + "detail": "false", + "source": "demo-fixture" + }, + "derived.stack": { + "detail": "C", + "source": "demo-fixture" + }, + "derived.stack_present": { + "detail": "false", + "source": "demo-fixture" + }, + "risk.doctor_gap": { + "detail": "false", + "source": "derived" + }, + "risk.risk_tier": { + "detail": "elevated", + "source": "derived" + } + }, + "repository_state": { + "local": { + "ahead": 0, + "behind": 0, + "branch": "main", + "dirty": false, + "dirty_path_count": 0, + "head": "f88738445f00c41b9915812a478d50676b4a5a3a9902d61950cc26be60d266d4", + "path": "/demo-workspace/lab/solstice-cairn", + "upstream": "origin/main", + "upstream_branch": "main", + "upstream_observation_source": "local_tracking_ref", + "upstream_remote": "origin" + }, + "observed_at": "2026-08-01T00:00:00+00:00", + "remote_default_branch": { + "reason": "no independent live remote read was performed by portfolio generation", + "reason_code": "not_requested", + "state": "unknown" + }, + "state": "observed", + "topology": { + "configured_path": "/demo-workspace/lab/solstice-cairn", + "kind": "working_repository", + "linked_worktree_count": 0, + "selection": { + "candidate_count": 0, + "reason": "independent remote-default evidence is not observed (state=unknown)", + "reason_code": "remote_default_branch_unavailable", + "source": "remote_default_branch", + "state": "unknown" + }, + "worktree_count": 1 + }, + "worktrees": [ + { + "ahead": 0, + "bare": false, + "behind": 0, + "branch": "main", + "detached": false, + "dirty": false, + "dirty_path_count": 0, + "head": "f88738445f00c41b9915812a478d50676b4a5a3a9902d61950cc26be60d266d4", + "path": "/demo-workspace/lab/solstice-cairn", + "state": "observed", + "upstream": "origin/main", + "upstream_branch": "main", + "upstream_observation_source": "local_tracking_ref", + "upstream_remote": "origin" + } + ] + }, + "risk": { + "context_risk": true, + "doctor_gap": false, + "path_risk": true, + "risk_factors": [ + "weak-context-active", + "investigate-override", + "no-run-instructions" + ], + "risk_summary": "3 risk factor(s): weak context quality, investigate override active, run instructions missing.", + "risk_tier": "elevated", + "security_risk": false + }, + "security": { + "alerts_available": false, + "code_scanning_critical": null, + "code_scanning_high": null, + "cohort_member": false, + "cohort_policy": "", + "coverage_state": "unknown", + "dependabot_critical": null, + "dependabot_high": null, + "dependabot_low": null, + "dependabot_medium": null, + "open_high_critical": 0, + "providers": {}, + "receipt_schema_version": "", + "receipt_state": "unknown", + "secret_scanning_open": null, + "source_produced_at": "" + }, + "warnings": [] + }, + { + "advisory": { + "legacy_category": "platform", + "legacy_context_quality": "full", + "legacy_status": "active", + "legacy_tool_provenance": "", + "notion_current_state": "", + "notion_momentum": "", + "notion_portfolio_call": "" + }, + "declared": { + "automation_eligible": false, + "category": "infrastructure", + "criticality": "medium", + "doctor_standard": "", + "intended_disposition": "", + "lifecycle_state": "active", + "maturity_program": "maintain", + "notes": "", + "operating_path": "maintain", + "owner": "demo-operator", + "purpose": "Shared platform service other demo projects depend on.", + "review_cadence": "monthly", + "target_maturity": "operating", + "team": "", + "tool_provenance": "claude-code" + }, + "derived": { + "activity_status": "active", + "archived": false, + "attention_state": "active-infra", + "context_file_count": 3, + "context_files": [ + "AGENTS.md", + "docs/current-state.md", + "docs/architecture.md" + ], + "context_quality": "full", + "current_state_present": true, + "has_ci": true, + "has_license": true, + "has_tests": true, + "known_risks_present": true, + "last_meaningful_activity_at": "2026-07-30T00:00:00+00:00", + "next_recommended_move_present": true, + "path_confidence": "high", + "path_override": "", + "path_rationale": "Stable path is Maintain from explicit operating path.", + "primary_context_file": "AGENTS.md", + "project_summary_present": true, + "readme_char_count": 900, + "release_count": 0, + "run_instructions_present": true, + "stack": [ + "Go" + ], + "stack_present": true + }, + "identity": { + "default_branch": "main", + "display_name": "Dovetail Forge", + "group_key": "platform", + "group_label": "Platform and Infrastructure", + "has_git": true, + "path": "platform/dovetail-forge", + "project_key": "platform/dovetail-forge", + "repo_full_name": "demo-org/dovetail-forge", + "section_label": "Platform and Infrastructure", + "section_marker": "platform/", + "top_level_dir": "platform" + }, + "provenance": { + "declared.category": { + "detail": "infrastructure", + "source": "demo-fixture" + }, + "declared.criticality": { + "detail": "medium", + "source": "demo-fixture" + }, + "declared.doctor_standard": { + "detail": "", + "source": "demo-fixture" + }, + "declared.intended_disposition": { + "detail": "", + "source": "demo-fixture" + }, + "declared.lifecycle_state": { + "detail": "active", + "source": "demo-fixture" + }, + "declared.maturity_program": { + "detail": "maintain", + "source": "demo-fixture" + }, + "declared.notes": { + "detail": "", + "source": "demo-fixture" + }, + "declared.operating_path": { + "detail": "explicit-operating-path", + "source": "normalized" + }, + "declared.owner": { + "detail": "demo-operator", + "source": "demo-fixture" + }, + "declared.purpose": { + "detail": "Shared platform service other demo projects depend on.", + "source": "demo-fixture" + }, + "declared.review_cadence": { + "detail": "monthly", + "source": "demo-fixture" + }, + "declared.target_maturity": { + "detail": "operating", + "source": "demo-fixture" + }, + "declared.team": { + "detail": "", + "source": "demo-fixture" + }, + "declared.tool_provenance": { + "detail": "claude-code", + "source": "demo-fixture" + }, + "derived.activity_status": { + "detail": "active", + "source": "demo-fixture" + }, + "derived.archived": { + "detail": "false", + "source": "demo-fixture" + }, + "derived.attention_state": { + "detail": "active-infra", + "source": "demo-fixture" + }, + "derived.context_files": { + "detail": "3", + "source": "demo-fixture" + }, + "derived.context_quality": { + "detail": "full", + "source": "demo-fixture" + }, + "derived.current_state_present": { + "detail": "true", + "source": "demo-fixture" + }, + "derived.known_risks_present": { + "detail": "true", + "source": "demo-fixture" + }, + "derived.last_meaningful_activity_at": { + "detail": "2026-07-30T00:00:00+00:00", + "source": "demo-fixture" + }, + "derived.next_recommended_move_present": { + "detail": "true", + "source": "demo-fixture" + }, + "derived.path_confidence": { + "detail": "high", + "source": "normalized" + }, + "derived.path_override": { + "detail": "", + "source": "normalized" + }, + "derived.path_rationale": { + "detail": "Stable path is Maintain from explicit operating path.", + "source": "normalized" + }, + "derived.primary_context_file": { + "detail": "AGENTS.md", + "source": "demo-fixture" + }, + "derived.project_summary_present": { + "detail": "true", + "source": "demo-fixture" + }, + "derived.run_instructions_present": { + "detail": "true", + "source": "demo-fixture" + }, + "derived.stack": { + "detail": "Go", + "source": "demo-fixture" + }, + "derived.stack_present": { + "detail": "true", + "source": "demo-fixture" + }, + "risk.doctor_gap": { + "detail": "false", + "source": "derived" + }, + "risk.risk_tier": { + "detail": "baseline", + "source": "derived" + } + }, + "repository_state": { + "local": { + "ahead": 0, + "behind": 0, + "branch": "main", + "dirty": false, + "dirty_path_count": 0, + "head": "e5ce82b2aef5e7bcce252f7d044b0c1f5d9c4b70b4e18e334e97f66015d57a51", + "path": "/demo-workspace/platform/dovetail-forge", + "upstream": "origin/main", + "upstream_branch": "main", + "upstream_observation_source": "local_tracking_ref", + "upstream_remote": "origin" + }, + "observed_at": "2026-08-01T00:00:00+00:00", + "remote_default_branch": { + "archived": false, + "default_branch": "main", + "head_sha": "e5ce82b2aef5e7bcce252f7d044b0c1f5d9c4b70b4e18e334e97f66015d57a51", + "observed_at": "2026-08-01T00:00:00+00:00", + "reason": null, + "reason_code": "observed", + "source": "github-graphql-default-branch-head-v1", + "state": "observed" + }, + "state": "observed", + "topology": { + "configured_path": "/demo-workspace/platform/dovetail-forge", + "kind": "working_repository", + "linked_worktree_count": 0, + "selection": { + "branch": "main", + "candidate_count": 1, + "head": "e5ce82b2aef5e7bcce252f7d044b0c1f5d9c4b70b4e18e334e97f66015d57a51", + "path": "/demo-workspace/platform/dovetail-forge", + "reason": null, + "reason_code": "unique_remote_head_match", + "source": "github-graphql-default-branch-head-v1", + "state": "selected" + }, + "worktree_count": 1 + }, + "worktrees": [ + { + "ahead": 0, + "bare": false, + "behind": 0, + "branch": "main", + "detached": false, + "dirty": false, + "dirty_path_count": 0, + "head": "e5ce82b2aef5e7bcce252f7d044b0c1f5d9c4b70b4e18e334e97f66015d57a51", + "path": "/demo-workspace/platform/dovetail-forge", + "state": "observed", + "upstream": "origin/main", + "upstream_branch": "main", + "upstream_observation_source": "local_tracking_ref", + "upstream_remote": "origin" + } + ] + }, + "risk": { + "context_risk": false, + "doctor_gap": false, + "path_risk": false, + "risk_factors": [], + "risk_summary": "No elevated risk factors.", + "risk_tier": "baseline", + "security_risk": false + }, + "security": { + "alerts_available": true, + "code_scanning_critical": 0, + "code_scanning_high": 0, + "cohort_member": true, + "cohort_policy": "portfolio-default-attention-v1", + "coverage_state": "complete", + "dependabot_critical": 0, + "dependabot_high": 0, + "dependabot_low": 4, + "dependabot_medium": 2, + "open_high_critical": 0, + "providers": { + "code_scanning": { + "completed": true, + "conditional": { + "requested": true, + "result": "modified" + }, + "counts": { + "critical": 0, + "high": 0, + "note": 0, + "warning": 0 + }, + "etag": null, + "http_classification": "success", + "http_status": 200, + "last_modified": null, + "observed_at": "2026-08-01T00:00:00+00:00", + "pagination_complete": true, + "reason": null, + "reason_code": "observed", + "state": "observed", + "zero_findings": true + }, + "dependabot": { + "completed": true, + "conditional": { + "requested": true, + "result": "modified" + }, + "counts": { + "critical": 0, + "high": 0, + "low": 4, + "medium": 2 + }, + "etag": null, + "http_classification": "success", + "http_status": 200, + "last_modified": null, + "observed_at": "2026-08-01T00:00:00+00:00", + "pagination_complete": true, + "reason": null, + "reason_code": "observed", + "state": "observed", + "zero_findings": false + }, + "secret_scanning": { + "completed": true, + "conditional": { + "requested": true, + "result": "modified" + }, + "counts": { + "open": 0 + }, + "etag": null, + "http_classification": "success", + "http_status": 200, + "last_modified": null, + "observed_at": "2026-08-01T00:00:00+00:00", + "pagination_complete": true, + "reason": null, + "reason_code": "observed", + "state": "observed", + "zero_findings": true + } + }, + "receipt_schema_version": "GitHubSecurityCoverageReceiptV1", + "receipt_state": "fresh", + "secret_scanning_open": 0, + "source_produced_at": "2026-08-01T00:00:00+00:00" + }, + "warnings": [] + }, + { + "advisory": { + "legacy_category": "studio", + "legacy_context_quality": "standard", + "legacy_status": "active", + "legacy_tool_provenance": "", + "notion_current_state": "", + "notion_momentum": "", + "notion_portfolio_call": "" + }, + "declared": { + "automation_eligible": false, + "category": "fun", + "criticality": "medium", + "doctor_standard": "", + "intended_disposition": "", + "lifecycle_state": "active", + "maturity_program": "maintain", + "notes": "", + "operating_path": "finish", + "owner": "demo-operator", + "purpose": "Product experiment maintained on a manual cadence.", + "review_cadence": "monthly", + "target_maturity": "operating", + "team": "", + "tool_provenance": "codex" + }, + "derived": { + "activity_status": "recent", + "archived": false, + "attention_state": "decision-needed", + "context_file_count": 2, + "context_files": [ + "AGENTS.md", + "docs/current-state.md" + ], + "context_quality": "standard", + "current_state_present": true, + "has_ci": true, + "has_license": true, + "has_tests": true, + "known_risks_present": true, + "last_meaningful_activity_at": "2026-07-11T00:00:00+00:00", + "next_recommended_move_present": true, + "path_confidence": "high", + "path_override": "", + "path_rationale": "Stable path is Finish from explicit operating path.", + "primary_context_file": "AGENTS.md", + "project_summary_present": true, + "readme_char_count": 1037, + "release_count": 0, + "run_instructions_present": true, + "stack": [ + "Ruby" + ], + "stack_present": true + }, + "identity": { + "default_branch": "main", + "display_name": "Kestrel Loom", + "group_key": "studio", + "group_label": "Studio Projects", + "has_git": true, + "path": "studio/kestrel-loom", + "project_key": "studio/kestrel-loom", + "repo_full_name": "demo-org/kestrel-loom", + "section_label": "Studio Projects", + "section_marker": "studio/", + "top_level_dir": "studio" + }, + "provenance": { + "declared.category": { + "detail": "fun", + "source": "demo-fixture" + }, + "declared.criticality": { + "detail": "medium", + "source": "demo-fixture" + }, + "declared.doctor_standard": { + "detail": "", + "source": "demo-fixture" + }, + "declared.intended_disposition": { + "detail": "", + "source": "demo-fixture" + }, + "declared.lifecycle_state": { + "detail": "active", + "source": "demo-fixture" + }, + "declared.maturity_program": { + "detail": "maintain", + "source": "demo-fixture" + }, + "declared.notes": { + "detail": "", + "source": "demo-fixture" + }, + "declared.operating_path": { + "detail": "explicit-operating-path", + "source": "normalized" + }, + "declared.owner": { + "detail": "demo-operator", + "source": "demo-fixture" + }, + "declared.purpose": { + "detail": "Product experiment maintained on a manual cadence.", + "source": "demo-fixture" + }, + "declared.review_cadence": { + "detail": "monthly", + "source": "demo-fixture" + }, + "declared.target_maturity": { + "detail": "operating", + "source": "demo-fixture" + }, + "declared.team": { + "detail": "", + "source": "demo-fixture" + }, + "declared.tool_provenance": { + "detail": "codex", + "source": "demo-fixture" + }, + "derived.activity_status": { + "detail": "recent", + "source": "demo-fixture" + }, + "derived.archived": { + "detail": "false", + "source": "demo-fixture" + }, + "derived.attention_state": { + "detail": "decision-needed", + "source": "demo-fixture" + }, + "derived.context_files": { + "detail": "2", + "source": "demo-fixture" + }, + "derived.context_quality": { + "detail": "standard", + "source": "demo-fixture" + }, + "derived.current_state_present": { + "detail": "true", + "source": "demo-fixture" + }, + "derived.known_risks_present": { + "detail": "true", + "source": "demo-fixture" + }, + "derived.last_meaningful_activity_at": { + "detail": "2026-07-11T00:00:00+00:00", + "source": "demo-fixture" + }, + "derived.next_recommended_move_present": { + "detail": "true", + "source": "demo-fixture" + }, + "derived.path_confidence": { + "detail": "high", + "source": "normalized" + }, + "derived.path_override": { + "detail": "", + "source": "normalized" + }, + "derived.path_rationale": { + "detail": "Stable path is Finish from explicit operating path.", + "source": "normalized" + }, + "derived.primary_context_file": { + "detail": "AGENTS.md", + "source": "demo-fixture" + }, + "derived.project_summary_present": { + "detail": "true", + "source": "demo-fixture" + }, + "derived.run_instructions_present": { + "detail": "true", + "source": "demo-fixture" + }, + "derived.stack": { + "detail": "Ruby", + "source": "demo-fixture" + }, + "derived.stack_present": { + "detail": "true", + "source": "demo-fixture" + }, + "risk.doctor_gap": { + "detail": "false", + "source": "derived" + }, + "risk.risk_tier": { + "detail": "moderate", + "source": "derived" + } + }, + "repository_state": { + "local": { + "ahead": 0, + "behind": 0, + "branch": "main", + "dirty": false, + "dirty_path_count": 0, + "head": "def1e22cfe8a27746f826754683cb03ed9a56dffacdf8552654a5d7dc026aa4b", + "path": "/demo-workspace/studio/kestrel-loom", + "upstream": "origin/main", + "upstream_branch": "main", + "upstream_observation_source": "local_tracking_ref", + "upstream_remote": "origin" + }, + "observed_at": "2026-08-01T00:00:00+00:00", + "remote_default_branch": { + "archived": false, + "default_branch": "main", + "head_sha": "def1e22cfe8a27746f826754683cb03ed9a56dffacdf8552654a5d7dc026aa4b", + "observed_at": "2026-08-01T00:00:00+00:00", + "reason": null, + "reason_code": "observed", + "source": "github-graphql-default-branch-head-v1", + "state": "observed" + }, + "state": "observed", + "topology": { + "configured_path": "/demo-workspace/studio/kestrel-loom", + "kind": "working_repository", + "linked_worktree_count": 0, + "selection": { + "branch": "main", + "candidate_count": 1, + "head": "def1e22cfe8a27746f826754683cb03ed9a56dffacdf8552654a5d7dc026aa4b", + "path": "/demo-workspace/studio/kestrel-loom", + "reason": null, + "reason_code": "unique_remote_head_match", + "source": "github-graphql-default-branch-head-v1", + "state": "selected" + }, + "worktree_count": 1 + }, + "worktrees": [ + { + "ahead": 0, + "bare": false, + "behind": 0, + "branch": "main", + "detached": false, + "dirty": false, + "dirty_path_count": 0, + "head": "def1e22cfe8a27746f826754683cb03ed9a56dffacdf8552654a5d7dc026aa4b", + "path": "/demo-workspace/studio/kestrel-loom", + "state": "observed", + "upstream": "origin/main", + "upstream_branch": "main", + "upstream_observation_source": "local_tracking_ref", + "upstream_remote": "origin" + } + ] + }, + "risk": { + "context_risk": false, + "doctor_gap": false, + "path_risk": false, + "risk_factors": [ + "active-high-severity-alerts" + ], + "risk_summary": "1 risk factor(s): open high/critical security alerts.", + "risk_tier": "moderate", + "security_risk": true + }, + "security": { + "alerts_available": false, + "code_scanning_critical": null, + "code_scanning_high": null, + "cohort_member": true, + "cohort_policy": "portfolio-default-attention-v1", + "coverage_state": "partial", + "dependabot_critical": 0, + "dependabot_high": 3, + "dependabot_low": 0, + "dependabot_medium": 1, + "open_high_critical": 3, + "providers": { + "code_scanning": { + "completed": false, + "conditional": { + "requested": false, + "result": "failed" + }, + "counts": null, + "etag": null, + "http_classification": "github_not_found", + "http_status": 404, + "last_modified": null, + "observed_at": "2026-08-01T00:00:00+00:00", + "pagination_complete": false, + "reason": "github_not_found", + "reason_code": "endpoint_unsupported", + "state": "not_found", + "zero_findings": null + }, + "dependabot": { + "completed": true, + "conditional": { + "requested": true, + "result": "modified" + }, + "counts": { + "critical": 0, + "high": 3, + "low": 0, + "medium": 1 + }, + "etag": null, + "http_classification": "success", + "http_status": 200, + "last_modified": null, + "observed_at": "2026-08-01T00:00:00+00:00", + "pagination_complete": true, + "reason": null, + "reason_code": "observed", + "state": "observed", + "zero_findings": false + }, + "secret_scanning": { + "completed": false, + "conditional": { + "requested": false, + "result": "failed" + }, + "counts": null, + "etag": null, + "http_classification": "github_not_found", + "http_status": 404, + "last_modified": null, + "observed_at": "2026-08-01T00:00:00+00:00", + "pagination_complete": false, + "reason": "github_not_found", + "reason_code": "endpoint_unsupported", + "state": "not_found", + "zero_findings": null + } + }, + "receipt_schema_version": "GitHubSecurityCoverageReceiptV1", + "receipt_state": "fresh", + "secret_scanning_open": null, + "source_produced_at": "2026-08-01T00:00:00+00:00" + }, + "warnings": [] + } + ], + "rollups": { + "decision": { + "decision_needed_count": 1, + "default_attention_count": 2 + }, + "risk_tier_counts": { + "baseline": 2, + "deferred": 0, + "elevated": 1, + "moderate": 1 + }, + "security": { + "code_scanning_observed_count": 1, + "code_scanning_zero_finding_count": 1, + "cohort_complete_count": 1, + "cohort_partial_count": 1, + "cohort_repository_count": 3, + "cohort_stale_count": 1, + "cohort_unknown_count": 0, + "complete_repo_count": 1, + "coverage_state": "partial", + "dependabot_observed_count": 2, + "dependabot_zero_finding_count": 0, + "partial_repo_count": 1, + "remote_default_branch_observed_count": 2, + "remote_default_branch_partial_count": 0, + "remote_default_branch_stale_count": 1, + "remote_default_branch_unavailable_count": 1, + "repos_with_open_high_critical": 1, + "scanned_count": 1, + "secret_scanning_observed_count": 1, + "secret_scanning_zero_finding_count": 1, + "stale_count": 1, + "total_open_critical": 0, + "total_open_high": 3, + "unavailable_count": 3, + "unknown_count": 1 + } + }, + "schema_version": "0.11.0", + "source_summary": { + "activity_status_counts": { + "active": 1, + "recent": 2, + "stale": 1 + }, + "archived_count": 0, + "attention_state_counts": { + "active-infra": 1, + "decision-needed": 1, + "manual-only": 1, + "parked": 1 + }, + "catalog_errors": [], + "catalog_warnings": [], + "checkout_collisions": { + "ambiguous_group_count": 0, + "discarded_checkout_count": 0, + "full_clone_group_count": 0, + "group_count": 0, + "groups": [], + "schema_version": "CheckoutCollisionSummaryV1", + "state": "observed" + }, + "context_quality_counts": { + "boilerplate": 1, + "full": 1, + "minimum-viable": 1, + "standard": 1 + }, + "duplicate_display_names": [], + "github_archived_count": 0, + "legacy_registry_rows": 4, + "notion_context_carried_forward": false, + "notion_context_rows": 0, + "project_count": 4, + "unresolved_duplicate_display_names": [], + "workspace_root": "/demo-workspace" + }, + "warnings": [], + "workspace_root": "/demo-workspace" +} diff --git a/fixtures/contracts/public-site-projection-v1/manifest.json b/fixtures/contracts/public-site-projection-v1/manifest.json new file mode 100644 index 00000000..d9a1522e --- /dev/null +++ b/fixtures/contracts/public-site-projection-v1/manifest.json @@ -0,0 +1,90 @@ +{ + "acceptance": { + "coverage_cases": [ + { + "case_id": "valid-current-schema", + "expected": "accept", + "source": "artifact" + }, + { + "case_id": "additive-canary", + "expected": "accept-ignore-addition", + "pointer": "/projects/0/additive_contract_canary", + "source": "artifact" + }, + { + "case_id": "missing-schema-version", + "expected": "reject", + "mutation": { + "op": "remove", + "path": "/schema_version" + }, + "source": "artifact" + }, + { + "case_id": "malformed-root", + "expected": "reject", + "source": "literal", + "value": [] + }, + { + "case_id": "non-allowlisted-project", + "expected": "aggregate-only", + "source": "artifact" + }, + { + "case_id": "missing-projects", + "expected": "reject", + "mutation": { + "op": "remove", + "path": "/projects" + }, + "source": "artifact" + }, + { + "case_id": "malformed-projects", + "expected": "reject", + "mutation": { + "op": "replace", + "path": "/projects", + "value": "invalid" + }, + "source": "artifact" + } + ], + "fail_closed_behavior": "unknown-project-identities-remain-anonymous", + "public_projection": { + "allowlisted_repo_slugs": [ + "dovetail-forge", + "kestrel-loom" + ], + "expected_curated_repo_slugs": [ + "dovetail-forge", + "kestrel-loom" + ] + } + }, + "consumer_profile": { + "compatibility_policy": "additive-0.x", + "id": "public-site-projection-v1" + }, + "contract_version": "ghra-portfolio-truth-portable.v1", + "fixture": { + "additive_canary_paths": [ + "contract_fixture", + "projects[0].additive_contract_canary" + ], + "evaluation_time": "2026-08-01T06:00:00+00:00", + "generated_at": "2026-08-01T00:00:00+00:00", + "producer_evidence": "absent", + "project_count": 4 + }, + "portfolio_truth_schema_version": "0.11.0", + "producer": { + "artifact_path": "fixtures/contracts/portable-consumers-v1/portfolio-truth.json", + "artifact_sha256": "8af46309e1b7a891edf7489dcbfa5971cc3453fe983b9a824098d87dc42105d3", + "generator": "src.portfolio_truth_contract_fixture:build_portable_contract_fixture", + "manifest_path": "fixtures/contracts/public-site-projection-v1/manifest.json", + "repository": "saagpatel/GithubRepoAuditor" + } +} diff --git a/scripts/generate_portfolio_truth_contract_fixture.py b/scripts/generate_portfolio_truth_contract_fixture.py index 2ecd77b7..e7adfabc 100644 --- a/scripts/generate_portfolio_truth_contract_fixture.py +++ b/scripts/generate_portfolio_truth_contract_fixture.py @@ -12,25 +12,42 @@ sys.path.insert(0, str(REPO_ROOT)) from src.portfolio_truth_contract_fixture import ( # noqa: E402 + CONSUMER_PROFILE_MANIFEST_PATHS, FIXTURE_RELATIVE_PATH, MANIFEST_RELATIVE_PATH, + PORTABLE_FIXTURE_RELATIVE_PATH, build_contract_fixture, + build_portable_contract_fixture, + consumer_profile_manifest_bytes, fixture_bytes, manifest_bytes, + portable_fixture_bytes, ) from src.portfolio_truth_validate import validate_truth_snapshot_payload # noqa: E402 def _expected_artifacts() -> tuple[tuple[Path, bytes], ...]: + profile_manifests = tuple( + ( + REPO_ROOT / manifest_path, + consumer_profile_manifest_bytes(profile_id), + ) + for profile_id, manifest_path in sorted( + CONSUMER_PROFILE_MANIFEST_PATHS.items() + ) + ) return ( (REPO_ROOT / FIXTURE_RELATIVE_PATH, fixture_bytes()), (REPO_ROOT / MANIFEST_RELATIVE_PATH, manifest_bytes()), + (REPO_ROOT / PORTABLE_FIXTURE_RELATIVE_PATH, portable_fixture_bytes()), + *profile_manifests, ) def _check() -> int: try: validate_truth_snapshot_payload(build_contract_fixture()) + validate_truth_snapshot_payload(build_portable_contract_fixture()) except ValueError as exc: print( f"Portable PortfolioTruth consumer contract is invalid: {exc}", diff --git a/src/portfolio_truth_contract_fixture.py b/src/portfolio_truth_contract_fixture.py index 679782fc..dc903956 100644 --- a/src/portfolio_truth_contract_fixture.py +++ b/src/portfolio_truth_contract_fixture.py @@ -17,6 +17,7 @@ from src.portfolio_truth_types import SCHEMA_VERSION CONTRACT_VERSION = "ghra-pcc-portfolio-truth.v1" +PORTABLE_CONTRACT_VERSION = "ghra-portfolio-truth-portable.v1" PRODUCER_REPOSITORY = "saagpatel/GithubRepoAuditor" CONSUMER_REPOSITORY = "saagpatel/PortfolioCommandCenter" FIXTURE_RELATIVE_PATH = ( @@ -25,6 +26,17 @@ MANIFEST_RELATIVE_PATH = ( "fixtures/contracts/portfolio-command-center-v1/manifest.json" ) +PORTABLE_FIXTURE_RELATIVE_PATH = ( + "fixtures/contracts/portable-consumers-v1/portfolio-truth.json" +) +CONSUMER_PROFILE_MANIFEST_PATHS = { + "operator-control-plane-v1": ( + "fixtures/contracts/operator-control-plane-v1/manifest.json" + ), + "public-site-projection-v1": ( + "fixtures/contracts/public-site-projection-v1/manifest.json" + ), +} # Fixed-clock values make the bytes durable. Consumers evaluate freshness # against EVALUATED_AT rather than wall time; runtime freshness behavior remains @@ -48,8 +60,7 @@ def _contract_project_specs() -> tuple[DemoProject, ...]: return tuple(by_codename[name] for name in _PROJECT_CODENAMES) -def build_contract_fixture() -> dict[str, Any]: - """Return the fixed-clock public-safe PortfolioTruth compatibility fixture.""" +def _build_fixture(contract_version: str) -> dict[str, Any]: fixture = build_snapshot( GENERATED_AT, project_specs=_contract_project_specs(), @@ -59,7 +70,7 @@ def build_contract_fixture() -> dict[str, Any]: # evidence; partial synthetic evidence would be invalid and misleading. fixture["producer"] = {} fixture["contract_fixture"] = { - "contract_version": CONTRACT_VERSION, + "contract_version": contract_version, "deterministic": True, "producer_evidence": "absent", "security_evidence_semantics": "synthetic-cross-receipt-state-matrix", @@ -70,6 +81,16 @@ def build_contract_fixture() -> dict[str, Any]: return fixture +def build_contract_fixture() -> dict[str, Any]: + """Return the byte-stable legacy PortfolioCommandCenter fixture.""" + return _build_fixture(CONTRACT_VERSION) + + +def build_portable_contract_fixture() -> dict[str, Any]: + """Return the shared public-safe fixture for additive consumer profiles.""" + return _build_fixture(PORTABLE_CONTRACT_VERSION) + + def canonical_json_bytes(payload: dict[str, Any]) -> bytes: """Serialize a contract artifact in the one canonical committed form.""" rendered = json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=False) @@ -84,6 +105,14 @@ def fixture_sha256() -> str: return hashlib.sha256(fixture_bytes()).hexdigest() +def portable_fixture_bytes() -> bytes: + return canonical_json_bytes(build_portable_contract_fixture()) + + +def portable_fixture_sha256() -> str: + return hashlib.sha256(portable_fixture_bytes()).hexdigest() + + def build_contract_manifest() -> dict[str, Any]: """Describe the portable artifact without a self-referential Git commit. @@ -126,3 +155,128 @@ def build_contract_manifest() -> dict[str, Any]: def manifest_bytes() -> bytes: return canonical_json_bytes(build_contract_manifest()) + + +def _profile_acceptance(profile_id: str) -> dict[str, Any]: + common_cases = [ + { + "case_id": "valid-current-schema", + "source": "artifact", + "expected": "accept", + }, + { + "case_id": "additive-canary", + "source": "artifact", + "pointer": "/projects/0/additive_contract_canary", + "expected": "accept-ignore-addition", + }, + { + "case_id": "missing-schema-version", + "source": "artifact", + "mutation": {"op": "remove", "path": "/schema_version"}, + "expected": "reject", + }, + { + "case_id": "malformed-root", + "source": "literal", + "value": [], + "expected": "reject", + }, + ] + if profile_id == "operator-control-plane-v1": + return { + "coverage_cases": [ + *common_cases, + { + "case_id": "contradictory-contract-envelope", + "source": "artifact", + "mutation": { + "op": "add", + "path": "/contract", + "value": { + "id": "ghra.portfolio_truth", + "version": "0.12.0", + "compatibility": "additive", + }, + }, + "expected": "reject", + }, + ], + "fail_closed_behavior": "incompatible-artifact-yields-unavailable-health", + } + if profile_id == "public-site-projection-v1": + return { + "coverage_cases": [ + *common_cases, + { + "case_id": "non-allowlisted-project", + "source": "artifact", + "expected": "aggregate-only", + }, + { + "case_id": "missing-projects", + "source": "artifact", + "mutation": {"op": "remove", "path": "/projects"}, + "expected": "reject", + }, + { + "case_id": "malformed-projects", + "source": "artifact", + "mutation": { + "op": "replace", + "path": "/projects", + "value": "invalid", + }, + "expected": "reject", + }, + ], + "public_projection": { + "allowlisted_repo_slugs": ["dovetail-forge", "kestrel-loom"], + "expected_curated_repo_slugs": [ + "dovetail-forge", + "kestrel-loom", + ], + }, + "fail_closed_behavior": "unknown-project-identities-remain-anonymous", + } + raise ValueError(f"Unknown PortfolioTruth consumer profile: {profile_id}") + + +def build_consumer_profile_manifest(profile_id: str) -> dict[str, Any]: + """Describe one consumer profile without exposing a private repository name.""" + manifest_path = CONSUMER_PROFILE_MANIFEST_PATHS.get(profile_id) + if manifest_path is None: + raise ValueError(f"Unknown PortfolioTruth consumer profile: {profile_id}") + return { + "contract_version": PORTABLE_CONTRACT_VERSION, + "producer": { + "repository": PRODUCER_REPOSITORY, + "generator": ( + "src.portfolio_truth_contract_fixture:" + "build_portable_contract_fixture" + ), + "manifest_path": manifest_path, + "artifact_path": PORTABLE_FIXTURE_RELATIVE_PATH, + "artifact_sha256": portable_fixture_sha256(), + }, + "consumer_profile": { + "id": profile_id, + "compatibility_policy": "additive-0.x", + }, + "portfolio_truth_schema_version": SCHEMA_VERSION, + "fixture": { + "generated_at": GENERATED_AT.isoformat(), + "evaluation_time": EVALUATED_AT.isoformat(), + "project_count": len(_PROJECT_CODENAMES), + "producer_evidence": "absent", + "additive_canary_paths": [ + "contract_fixture", + "projects[0].additive_contract_canary", + ], + }, + "acceptance": _profile_acceptance(profile_id), + } + + +def consumer_profile_manifest_bytes(profile_id: str) -> bytes: + return canonical_json_bytes(build_consumer_profile_manifest(profile_id)) diff --git a/src/portfolio_truth_validate.py b/src/portfolio_truth_validate.py index 061dc46a..1dd1e4c5 100644 --- a/src/portfolio_truth_validate.py +++ b/src/portfolio_truth_validate.py @@ -1652,9 +1652,17 @@ def validate_truth_snapshot_payload( contract_fixture = payload.get("contract_fixture") is_documented_contract_matrix = ( isinstance(contract_fixture, Mapping) - and contract_fixture + and contract_fixture.get("contract_version") + in { + "ghra-pcc-portfolio-truth.v1", + "ghra-portfolio-truth-portable.v1", + } + and { + key: value + for key, value in contract_fixture.items() + if key != "contract_version" + } == { - "contract_version": "ghra-pcc-portfolio-truth.v1", "deterministic": True, "producer_evidence": "absent", "security_evidence_semantics": ( diff --git a/tests/test_portfolio_truth_contract_fixture.py b/tests/test_portfolio_truth_contract_fixture.py index 0fe44795..a663887e 100644 --- a/tests/test_portfolio_truth_contract_fixture.py +++ b/tests/test_portfolio_truth_contract_fixture.py @@ -18,16 +18,23 @@ from src.portfolio_truth_precedence import PRECEDENCE_MATRIX from src.portfolio_truth_provenance import REQUIRED_PROJECT_PROVENANCE_KEYS from src.portfolio_truth_contract_fixture import ( + CONSUMER_PROFILE_MANIFEST_PATHS, CONTRACT_VERSION, EVALUATED_AT, FIXTURE_RELATIVE_PATH, GENERATED_AT, MANIFEST_RELATIVE_PATH, + PORTABLE_CONTRACT_VERSION, + PORTABLE_FIXTURE_RELATIVE_PATH, PRODUCER_REPOSITORY, build_contract_fixture, build_contract_manifest, + build_consumer_profile_manifest, + build_portable_contract_fixture, + consumer_profile_manifest_bytes, fixture_bytes, manifest_bytes, + portable_fixture_bytes, ) from src.portfolio_truth_reconcile import _build_security_fields from src.portfolio_truth_sources import WORKSPACE_DISCOVERY_POLICY_VERSION @@ -49,6 +56,78 @@ def test_committed_contract_artifacts_match_the_deterministic_generator() -> Non assert Path(MANIFEST_RELATIVE_PATH).read_bytes() == manifest_bytes() +def test_portable_profile_artifacts_match_the_deterministic_generator() -> None: + assert ( + Path(PORTABLE_FIXTURE_RELATIVE_PATH).read_bytes() + == portable_fixture_bytes() + ) + for profile_id, manifest_path in CONSUMER_PROFILE_MANIFEST_PATHS.items(): + assert Path(manifest_path).read_bytes() == consumer_profile_manifest_bytes( + profile_id + ) + + +def test_portable_profiles_share_one_public_safe_artifact() -> None: + fixture = build_portable_contract_fixture() + manifests = [ + build_consumer_profile_manifest(profile_id) + for profile_id in sorted(CONSUMER_PROFILE_MANIFEST_PATHS) + ] + + validate_truth_snapshot_payload(fixture) + assert fixture["contract_fixture"]["contract_version"] == ( + PORTABLE_CONTRACT_VERSION + ) + assert { + manifest["producer"]["artifact_path"] for manifest in manifests + } == {PORTABLE_FIXTURE_RELATIVE_PATH} + assert len( + {manifest["producer"]["artifact_sha256"] for manifest in manifests} + ) == 1 + + rendered = json.dumps(manifests, sort_keys=True).lower() + assert "personal-ops" not in rendered + assert "portfolio-index" not in rendered + assert "/users/" not in rendered + + +def test_portable_profiles_bind_fixed_clock_additions_and_fail_closed_cases() -> None: + operator = build_consumer_profile_manifest("operator-control-plane-v1") + public_site = build_consumer_profile_manifest("public-site-projection-v1") + + for manifest in (operator, public_site): + assert manifest["fixture"]["generated_at"] == GENERATED_AT.isoformat() + assert manifest["fixture"]["evaluation_time"] == EVALUATED_AT.isoformat() + assert manifest["fixture"]["additive_canary_paths"] + cases = { + case["case_id"]: case for case in manifest["acceptance"]["coverage_cases"] + } + assert cases["valid-current-schema"]["expected"] == "accept" + assert cases["additive-canary"]["expected"] == ( + "accept-ignore-addition" + ) + assert cases["missing-schema-version"]["expected"] == "reject" + assert cases["malformed-root"]["expected"] == "reject" + assert manifest["acceptance"]["fail_closed_behavior"] + + projection = public_site["acceptance"]["public_projection"] + assert projection["allowlisted_repo_slugs"] == [ + "dovetail-forge", + "kestrel-loom", + ] + assert projection["expected_curated_repo_slugs"] == ( + projection["allowlisted_repo_slugs"] + ) + + +def test_undocumented_portable_matrix_marker_cannot_enable_synthetic_rows() -> None: + fixture = build_portable_contract_fixture() + fixture["contract_fixture"]["contract_version"] = "unrecognized-contract.v1" + + with pytest.raises(ValueError, match="source time is inconsistent"): + validate_truth_snapshot_payload(fixture) + + def test_manifest_binds_schema_generator_and_fixture_digest() -> None: manifest = json.loads(Path(MANIFEST_RELATIVE_PATH).read_text()) fixture_raw = Path(FIXTURE_RELATIVE_PATH).read_bytes() From 74e7b44738081f73bd3cda71e1ee805d83fb9abd Mon Sep 17 00:00:00 2001 From: saagpatel Date: Tue, 4 Aug 2026 22:29:54 -0700 Subject: [PATCH 2/2] test(portfolio): keep contract profiles consumer-neutral --- tests/test_portfolio_truth_contract_fixture.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/test_portfolio_truth_contract_fixture.py b/tests/test_portfolio_truth_contract_fixture.py index a663887e..81f7199e 100644 --- a/tests/test_portfolio_truth_contract_fixture.py +++ b/tests/test_portfolio_truth_contract_fixture.py @@ -84,10 +84,16 @@ def test_portable_profiles_share_one_public_safe_artifact() -> None: assert len( {manifest["producer"]["artifact_sha256"] for manifest in manifests} ) == 1 + assert { + manifest["consumer_profile"]["id"] for manifest in manifests + } == { + "operator-control-plane-v1", + "public-site-projection-v1", + } rendered = json.dumps(manifests, sort_keys=True).lower() - assert "personal-ops" not in rendered - assert "portfolio-index" not in rendered + assert "consumer_repository" not in rendered + assert "consumer_name" not in rendered assert "/users/" not in rendered