diff --git a/docs/demo-proof/public-fixture/README.md b/docs/demo-proof/public-fixture/README.md index ac3fec0..d8b44ad 100644 --- a/docs/demo-proof/public-fixture/README.md +++ b/docs/demo-proof/public-fixture/README.md @@ -35,17 +35,41 @@ From the sibling Portfolio Command Center repo: ```sh pnpm install -pnpm demo:desktop +pnpm demo:desktop:fixture ``` -Then set the output directory in the app header to: +The fixture launch script preloads the public-safe fixture output directory in +the app header. -```text -../GithubRepoAuditor/output/demo -``` +Use `pnpm demo:desktop` only for manual live or custom-output review. Do not use +the live local default output directory for public recording. + +## Captured Frames + +Public-safe frames are included under `screenshots/`: + +- `00-ops-tauri-window.png` - desktop shell proof. +- `01-portfolio.png` - portfolio table. +- `02-risk-security.png` - risk and security posture. +- `03-burndown.png` - grouped remediation view. +- `04-trends.png` - history and security drift. +- `05-weekly-digest.png` - weekly digest and next move. + +The frames show only fixture repos (`RepoA`, `RepoB`, `RepoC`), synthetic +packages, synthetic advisory ids, relative fixture paths, and the fixture output +directory. ## Safety Claim This package proves the demo can be produced from fixture data. It does not prove that a recording is visually redacted. A final public recording still needs a human pass for frame-level privacy review. + +## What Stays Private + +Do not publish live local portfolio output, real repo names, local absolute +paths, security findings from the real portfolio, terminals, account menus, +Notion, email, calendar, Slack, bridge-db, personal-ops, SecondBrain, tokens, +cookies, env values, or raw agent/session state. The public asset is the pattern: +fixture-backed truth, visible risk, grouped remediation, trend evidence, and one +operator-approved next move. diff --git a/docs/demo-proof/public-fixture/RECORDING-CHECKLIST.md b/docs/demo-proof/public-fixture/RECORDING-CHECKLIST.md index eac5cbf..39a1d21 100644 --- a/docs/demo-proof/public-fixture/RECORDING-CHECKLIST.md +++ b/docs/demo-proof/public-fixture/RECORDING-CHECKLIST.md @@ -5,8 +5,8 @@ Use this checklist for a public-safe Portfolio Command Center recording. ## Preflight - [ ] Run `make demo` from `GithubRepoAuditor`. -- [ ] Run `pnpm demo:desktop` from `PortfolioCommandCenter`. -- [ ] Point Portfolio Command Center at `GithubRepoAuditor/output/demo`. +- [ ] Run `pnpm demo:desktop:fixture` from `PortfolioCommandCenter`. +- [ ] Confirm Portfolio Command Center is pointed at the fixture output directory. - [ ] Confirm the visible data is fixture data, not the private live portfolio. - [ ] Hide terminals, path bars, desktop clutter, account menus, and notification banners. @@ -30,3 +30,9 @@ Use this checklist for a public-safe Portfolio Command Center recording. - real security advisory details; - Notion, email, calendar, Slack, bridge-db, or SecondBrain content; - terminal scrollback, env vars, tokens, cookies, or config files. + +## Existing Public-Safe Frames + +Use the included `screenshots/` frames as still-image evidence or as the visual +source for a website case-study block. If recording new video, treat these +frames as the reference for what safe output looks like. diff --git a/docs/demo-proof/public-fixture/SUMMARY.md b/docs/demo-proof/public-fixture/SUMMARY.md index d39f5b5..b188292 100644 --- a/docs/demo-proof/public-fixture/SUMMARY.md +++ b/docs/demo-proof/public-fixture/SUMMARY.md @@ -1,18 +1,29 @@ # Public Fixture Demo Summary -Status: fixture proof package, pending visual capture. +Status: fixture proof package with public-safe visual capture. This package establishes the safe public data path for the Operator OS / Portfolio Command Center demo: - fixture input: `fixtures/demo/sample-report.json`; - generated artifacts: `output/demo/`, including the PortfolioCommandCenter - `projects` schema, weekly digest, burndown, trend snapshots, and empty - proposal queue; + schema `0.7.0` `projects` payload, weekly digest, burndown, trend snapshots, + and empty proposal queue; - desktop consumer: `PortfolioCommandCenter` pointed at `output/demo`; - private services required: none; - live writes performed: none. -The next step before publishing is to capture screenshots or video frames from -Portfolio Command Center while it is pointed at the fixture output directory, -then add those images to this package. +Captured public-safe frames: + +- `screenshots/00-ops-tauri-window.png`: Tauri desktop shell reading the fixture + output directory. +- `screenshots/01-portfolio.png`: Portfolio tab. +- `screenshots/02-risk-security.png`: Risk + Security tab. +- `screenshots/03-burndown.png`: Burndown tab. +- `screenshots/04-trends.png`: Trends tab. +- `screenshots/05-weekly-digest.png`: Weekly Digest tab. + +The tab frames were captured from the PortfolioCommandCenter React surface with +Tauri IPC mocked to the same fixture files under `output/demo/`. The desktop +shell frame was captured from the live Tauri window launched with the fixture +output path preselected. diff --git a/docs/demo-proof/public-fixture/VERIFICATION-NOTES.md b/docs/demo-proof/public-fixture/VERIFICATION-NOTES.md new file mode 100644 index 0000000..40f94a1 --- /dev/null +++ b/docs/demo-proof/public-fixture/VERIFICATION-NOTES.md @@ -0,0 +1,46 @@ +# Public Fixture Verification Notes + +Date: 2026-06-27 + +## Fixture Truth + +- Fixture input: `fixtures/demo/sample-report.json`. +- Generated output directory: `output/demo`. +- Portfolio truth schema: `0.7.0`. +- Visible project names: `RepoA`, `RepoB`, `RepoC`. +- Visible workspace root: `fixtures/demo`. + +## Commands Run + +```sh +./.venv/bin/python scripts/build_demo_artifacts.py +./.venv/bin/python scripts/validate_proof_package.py docs/demo-proof/public-fixture/proof-package.json +pnpm typecheck +pnpm test +pnpm build +pnpm demo:desktop:fixture +``` + +## Visual Capture + +- Desktop shell frame captured from the live Tauri window with `screencapture -l`. +- Tab frames captured from the PortfolioCommandCenter React app served by Vite, + with Tauri IPC mocked to the same fixture files in `output/demo`. +- Captured tabs: Portfolio, Risk + Security, Burndown, Trends, Weekly Digest. + +## Public-Safety Review + +Manual inspection confirmed the retained frames show fixture labels only: + +- repo names are `RepoA`, `RepoB`, `RepoC`; +- paths are relative fixture paths such as `fixtures/demo/RepoA`; +- app output directory is the public fixture output directory; +- advisories and packages are synthetic (`demo-runtime`, `demo-ui-kit`, + `GHSA-DEMO-0001`, `GHSA-DEMO-0002`); +- no terminal, browser chrome, account menu, local absolute path, token, email, + calendar, Slack, Notion row, bridge-db row, personal-ops data, SecondBrain + content, or real security finding is visible. + +Known visible caveat: the fixture date is intentionally `2026-04-12`, so the app +shows a stale-data banner on 2026-06-27. That banner is public-safe, but a future +polish pass may choose to make fixture freshness deterministic for public demos. diff --git a/docs/demo-proof/public-fixture/WEBSITE-CONTENT.md b/docs/demo-proof/public-fixture/WEBSITE-CONTENT.md new file mode 100644 index 0000000..4513f57 --- /dev/null +++ b/docs/demo-proof/public-fixture/WEBSITE-CONTENT.md @@ -0,0 +1,49 @@ +# Website-Ready Demo Block + +## Operator OS Demo: Portfolio Command Center + +Portfolio Command Center is the public-safe cockpit for the Operator OS idea: a +local-first truth engine turns a small repo portfolio into risk posture, +security burndown, trend evidence, and one operator-approved next move. + +This demo uses fixture data only. The app is pointed at generated +GithubRepoAuditor artifacts under `output/demo`, not the private live portfolio. +The screenshots show three synthetic repos (`RepoA`, `RepoB`, `RepoC`), two +synthetic advisory groups, and a weekly digest that reduces the fixture +portfolio to one decision. + +### What The Frames Show + +- Portfolio: each project carries risk, attention state, context quality, tool + provenance, and open high/critical alert counts. +- Risk + Security: elevated projects and alert totals become a portfolio-level + attention map. +- Burndown: two synthetic advisories are grouped by the package fix that clears + the affected repos. +- Trends: fixture history shows whether risk is improving or getting noisier. +- Weekly Digest: the system produces one headline, one decision, and one next + move. + +### What Stays Private + +The public demo does not expose the real local portfolio, real repo names, local +absolute paths, live security findings, terminals, account state, Notion, email, +calendar, Slack, bridge-db, personal-ops, SecondBrain, tokens, cookies, env +values, or raw agent/session records. The shareable product is the operating +pattern: generated truth, visible risk, grouped remediation, trend evidence, and +human approval gates. + +### Suggested Page Copy + +Operator OS is the missing control plane for AI-assisted builders: it turns +scattered agent work and repo sprawl into verified truth, visible risk, and one +operator-approved next move. + +Pair the copy with these local assets: + +- `screenshots/00-ops-tauri-window.png` +- `screenshots/01-portfolio.png` +- `screenshots/02-risk-security.png` +- `screenshots/03-burndown.png` +- `screenshots/04-trends.png` +- `screenshots/05-weekly-digest.png` diff --git a/docs/demo-proof/public-fixture/proof-package.json b/docs/demo-proof/public-fixture/proof-package.json index 09c60d2..cb405c9 100644 --- a/docs/demo-proof/public-fixture/proof-package.json +++ b/docs/demo-proof/public-fixture/proof-package.json @@ -12,14 +12,14 @@ "commands": [ "make demo", "python scripts/validate_proof_package.py docs/demo-proof/public-fixture/proof-package.json", - "pnpm demo:desktop" + "pnpm demo:desktop:fixture" ] }, "source_state": { "source_data_mode": "fixture", "fixture": "../../../fixtures/demo/sample-report.json", "generated_output_dir": "../../../output/demo", - "source_truth_schema": "demo-pcc-v1", + "source_truth_schema": "0.7.0", "freshness_window_hours": null }, "claims": [ @@ -72,15 +72,22 @@ }, { "id": "visual-capture", - "statement": "Final public screenshots or video frames still need a frame-level privacy review after capture.", - "status": "partial", + "statement": "Public-safe Portfolio Command Center frames were captured from the fixture-backed desktop shell and React tab surfaces.", + "status": "passed", "evidence": [ - "summary" + "summary", + "verification-notes", + "screenshot-ops-shell", + "screenshot-portfolio", + "screenshot-risk-security", + "screenshot-burndown", + "screenshot-trends", + "screenshot-weekly-digest" ] } ], "verification": { - "overall": "partial", + "overall": "passed", "checks": [ { "name": "manifest references fixture input and generated output paths", @@ -88,13 +95,12 @@ }, { "name": "visual capture from Portfolio Command Center", - "status": "partial" + "status": "passed" } ], "missing_receipts": [], "known_gaps": [ - "This package proves the data path and recording boundary. It does not include captured public screenshots yet.", - "Portfolio Command Center must be pointed at output/demo during recording." + "The fixture date is intentionally static, so screenshots show the app stale-data banner when viewed after the fixture date." ] }, "safety": { @@ -222,6 +228,62 @@ "path": "../../../output/demo/portfolio-warehouse.db", "description": "Generated demo warehouse snapshot.", "required": true + }, + { + "id": "verification-notes", + "kind": "verification-notes", + "path": "VERIFICATION-NOTES.md", + "description": "Commands run, capture method, and public-safety review notes.", + "required": true + }, + { + "id": "website-content", + "kind": "website-copy", + "path": "WEBSITE-CONTENT.md", + "description": "Website-ready Operator OS demo content block and what-stays-private copy.", + "required": true + }, + { + "id": "screenshot-ops-shell", + "kind": "screenshot", + "path": "screenshots/00-ops-tauri-window.png", + "description": "Tauri desktop shell pointed at fixture output.", + "required": true + }, + { + "id": "screenshot-portfolio", + "kind": "screenshot", + "path": "screenshots/01-portfolio.png", + "description": "Portfolio tab rendered from fixture truth.", + "required": true + }, + { + "id": "screenshot-risk-security", + "kind": "screenshot", + "path": "screenshots/02-risk-security.png", + "description": "Risk and Security tab rendered from fixture truth.", + "required": true + }, + { + "id": "screenshot-burndown", + "kind": "screenshot", + "path": "screenshots/03-burndown.png", + "description": "Burndown tab rendered from fixture security burndown.", + "required": true + }, + { + "id": "screenshot-trends", + "kind": "screenshot", + "path": "screenshots/04-trends.png", + "description": "Trends tab rendered from fixture truth history.", + "required": true + }, + { + "id": "screenshot-weekly-digest", + "kind": "screenshot", + "path": "screenshots/05-weekly-digest.png", + "description": "Weekly Digest tab rendered from fixture digest.", + "required": true } ] } diff --git a/docs/demo-proof/public-fixture/screenshots/00-ops-tauri-window.png b/docs/demo-proof/public-fixture/screenshots/00-ops-tauri-window.png new file mode 100644 index 0000000..5920502 Binary files /dev/null and b/docs/demo-proof/public-fixture/screenshots/00-ops-tauri-window.png differ diff --git a/docs/demo-proof/public-fixture/screenshots/01-portfolio.png b/docs/demo-proof/public-fixture/screenshots/01-portfolio.png new file mode 100644 index 0000000..94a18b4 Binary files /dev/null and b/docs/demo-proof/public-fixture/screenshots/01-portfolio.png differ diff --git a/docs/demo-proof/public-fixture/screenshots/02-risk-security.png b/docs/demo-proof/public-fixture/screenshots/02-risk-security.png new file mode 100644 index 0000000..3881576 Binary files /dev/null and b/docs/demo-proof/public-fixture/screenshots/02-risk-security.png differ diff --git a/docs/demo-proof/public-fixture/screenshots/03-burndown.png b/docs/demo-proof/public-fixture/screenshots/03-burndown.png new file mode 100644 index 0000000..1c0391c Binary files /dev/null and b/docs/demo-proof/public-fixture/screenshots/03-burndown.png differ diff --git a/docs/demo-proof/public-fixture/screenshots/04-trends.png b/docs/demo-proof/public-fixture/screenshots/04-trends.png new file mode 100644 index 0000000..8f289fd Binary files /dev/null and b/docs/demo-proof/public-fixture/screenshots/04-trends.png differ diff --git a/docs/demo-proof/public-fixture/screenshots/05-weekly-digest.png b/docs/demo-proof/public-fixture/screenshots/05-weekly-digest.png new file mode 100644 index 0000000..d8b18ba Binary files /dev/null and b/docs/demo-proof/public-fixture/screenshots/05-weekly-digest.png differ diff --git a/output/demo/dashboard-sample-user-2026-04-12.html b/output/demo/dashboard-sample-user-2026-04-12.html index b46a418..030f82f 100644 --- a/output/demo/dashboard-sample-user-2026-04-12.html +++ b/output/demo/dashboard-sample-user-2026-04-12.html @@ -723,7 +723,7 @@

Writeback Results

Tech Radar

- +
LanguageReposTrendCategory
TypeScript44▁▂▆▆▆▆▆▇▇█Adopt
Rust32▁▁▁▁▁▁▁███Hold
Python19▁▂▄▄▄▄▄▅██Adopt
Swift16▅▅▅▅▅▅▅▅▅▅Hold
Shell4▁▁▁▁▂▂▂▂██Trial
Makefile3▁█▆▆▆▆▆▆▆▆Trial
GDScript3▅▅▅▅▅▅▅▅▅▅Hold
JavaScript2▅▅▅▅▅▅▅▅▅▅Hold
PLpgSQL1▅▅▅▅▅▅▅▅▅▅Hold
Go1▁▁████████Trial
Python2Hold
TypeScript1Hold
diff --git a/output/demo/demo-report.json b/output/demo/demo-report.json index 5d48864..70e6f77 100644 --- a/output/demo/demo-report.json +++ b/output/demo/demo-report.json @@ -553,5 +553,23 @@ "next_best_action_rationale": null } } + ], + "language_trends": [ + { + "language": "Python", + "current_count": 2, + "category": "Hold", + "repos_per_run": [ + 2 + ] + }, + { + "language": "TypeScript", + "current_count": 1, + "category": "Hold", + "repos_per_run": [ + 1 + ] + } ] } \ No newline at end of file diff --git a/output/demo/demo-workbook.xlsx b/output/demo/demo-workbook.xlsx index c16bfb7..a8cc36d 100644 Binary files a/output/demo/demo-workbook.xlsx and b/output/demo/demo-workbook.xlsx differ diff --git a/output/demo/portfolio-truth-2026-04-05T120000Z.json b/output/demo/portfolio-truth-2026-04-05T120000Z.json index 17f99f4..a9db093 100644 --- a/output/demo/portfolio-truth-2026-04-05T120000Z.json +++ b/output/demo/portfolio-truth-2026-04-05T120000Z.json @@ -1,7 +1,48 @@ { - "schema_version": "demo-pcc-v1", + "schema_version": "0.7.0", "generated_at": "2026-04-05T12:00:00+00:00", "workspace_root": "fixtures/demo", + "source_summary": { + "workspace_root": "fixtures/demo", + "project_count": 3, + "catalog_errors": [], + "catalog_warnings": [], + "legacy_registry_rows": 3, + "notion_context_rows": 0, + "context_quality_counts": { + "full": 1, + "minimum-viable": 2 + }, + "registry_status_counts": { + "active": 1, + "recent": 1, + "parked": 1 + }, + "attention_state_counts": { + "active-product": 1, + "decision-needed": 2 + }, + "duplicate_display_names": [], + "unresolved_duplicate_display_names": [] + }, + "precedence_matrix": { + "identity": [ + "fixture" + ], + "declared": [ + "fixture" + ], + "derived": [ + "fixture report" + ], + "risk": [ + "fixture report" + ], + "security": [ + "fixture report" + ] + }, + "warnings": [], "projects": [ { "identity": { @@ -72,7 +113,8 @@ "dependabot_low": 0, "secret_scanning_open": 0, "code_scanning_critical": 0, - "code_scanning_high": 0 + "code_scanning_high": 0, + "open_high_critical": 0 }, "advisory": { "legacy_status": "shipped", @@ -152,7 +194,8 @@ "dependabot_low": 1, "secret_scanning_open": 0, "code_scanning_critical": 0, - "code_scanning_high": 0 + "code_scanning_high": 0, + "open_high_critical": 1 }, "advisory": { "legacy_status": "functional", @@ -232,7 +275,8 @@ "dependabot_low": 0, "secret_scanning_open": 0, "code_scanning_critical": 0, - "code_scanning_high": 1 + "code_scanning_high": 1, + "open_high_critical": 3 }, "advisory": { "legacy_status": "wip", @@ -241,5 +285,23 @@ "legacy_tool_provenance": "sample" } } - ] + ], + "rollups": { + "risk_tier_counts": { + "elevated": 0, + "moderate": 2, + "baseline": 1, + "deferred": 0 + }, + "security": { + "scanned_count": 3, + "repos_with_open_high_critical": 2, + "total_open_high": 3, + "total_open_critical": 1 + }, + "decision": { + "decision_needed_count": 2, + "default_attention_count": 3 + } + } } \ No newline at end of file diff --git a/output/demo/portfolio-truth-2026-04-12T120000Z.json b/output/demo/portfolio-truth-2026-04-12T120000Z.json index 0ea81bf..ee254e4 100644 --- a/output/demo/portfolio-truth-2026-04-12T120000Z.json +++ b/output/demo/portfolio-truth-2026-04-12T120000Z.json @@ -1,7 +1,48 @@ { - "schema_version": "demo-pcc-v1", + "schema_version": "0.7.0", "generated_at": "2026-04-12T12:00:00+00:00", "workspace_root": "fixtures/demo", + "source_summary": { + "workspace_root": "fixtures/demo", + "project_count": 3, + "catalog_errors": [], + "catalog_warnings": [], + "legacy_registry_rows": 3, + "notion_context_rows": 0, + "context_quality_counts": { + "full": 1, + "minimum-viable": 2 + }, + "registry_status_counts": { + "active": 1, + "recent": 1, + "parked": 1 + }, + "attention_state_counts": { + "active-product": 1, + "decision-needed": 2 + }, + "duplicate_display_names": [], + "unresolved_duplicate_display_names": [] + }, + "precedence_matrix": { + "identity": [ + "fixture" + ], + "declared": [ + "fixture" + ], + "derived": [ + "fixture report" + ], + "risk": [ + "fixture report" + ], + "security": [ + "fixture report" + ] + }, + "warnings": [], "projects": [ { "identity": { @@ -72,7 +113,8 @@ "dependabot_low": 0, "secret_scanning_open": 0, "code_scanning_critical": 0, - "code_scanning_high": 0 + "code_scanning_high": 0, + "open_high_critical": 0 }, "advisory": { "legacy_status": "shipped", @@ -152,7 +194,8 @@ "dependabot_low": 1, "secret_scanning_open": 0, "code_scanning_critical": 0, - "code_scanning_high": 0 + "code_scanning_high": 0, + "open_high_critical": 1 }, "advisory": { "legacy_status": "functional", @@ -232,7 +275,8 @@ "dependabot_low": 0, "secret_scanning_open": 0, "code_scanning_critical": 0, - "code_scanning_high": 1 + "code_scanning_high": 1, + "open_high_critical": 3 }, "advisory": { "legacy_status": "wip", @@ -241,5 +285,23 @@ "legacy_tool_provenance": "sample" } } - ] + ], + "rollups": { + "risk_tier_counts": { + "elevated": 2, + "moderate": 0, + "baseline": 1, + "deferred": 0 + }, + "security": { + "scanned_count": 3, + "repos_with_open_high_critical": 2, + "total_open_high": 3, + "total_open_critical": 1 + }, + "decision": { + "decision_needed_count": 2, + "default_attention_count": 3 + } + } } \ No newline at end of file diff --git a/output/demo/portfolio-truth-latest.json b/output/demo/portfolio-truth-latest.json index 0ea81bf..ee254e4 100644 --- a/output/demo/portfolio-truth-latest.json +++ b/output/demo/portfolio-truth-latest.json @@ -1,7 +1,48 @@ { - "schema_version": "demo-pcc-v1", + "schema_version": "0.7.0", "generated_at": "2026-04-12T12:00:00+00:00", "workspace_root": "fixtures/demo", + "source_summary": { + "workspace_root": "fixtures/demo", + "project_count": 3, + "catalog_errors": [], + "catalog_warnings": [], + "legacy_registry_rows": 3, + "notion_context_rows": 0, + "context_quality_counts": { + "full": 1, + "minimum-viable": 2 + }, + "registry_status_counts": { + "active": 1, + "recent": 1, + "parked": 1 + }, + "attention_state_counts": { + "active-product": 1, + "decision-needed": 2 + }, + "duplicate_display_names": [], + "unresolved_duplicate_display_names": [] + }, + "precedence_matrix": { + "identity": [ + "fixture" + ], + "declared": [ + "fixture" + ], + "derived": [ + "fixture report" + ], + "risk": [ + "fixture report" + ], + "security": [ + "fixture report" + ] + }, + "warnings": [], "projects": [ { "identity": { @@ -72,7 +113,8 @@ "dependabot_low": 0, "secret_scanning_open": 0, "code_scanning_critical": 0, - "code_scanning_high": 0 + "code_scanning_high": 0, + "open_high_critical": 0 }, "advisory": { "legacy_status": "shipped", @@ -152,7 +194,8 @@ "dependabot_low": 1, "secret_scanning_open": 0, "code_scanning_critical": 0, - "code_scanning_high": 0 + "code_scanning_high": 0, + "open_high_critical": 1 }, "advisory": { "legacy_status": "functional", @@ -232,7 +275,8 @@ "dependabot_low": 0, "secret_scanning_open": 0, "code_scanning_critical": 0, - "code_scanning_high": 1 + "code_scanning_high": 1, + "open_high_critical": 3 }, "advisory": { "legacy_status": "wip", @@ -241,5 +285,23 @@ "legacy_tool_provenance": "sample" } } - ] + ], + "rollups": { + "risk_tier_counts": { + "elevated": 2, + "moderate": 0, + "baseline": 1, + "deferred": 0 + }, + "security": { + "scanned_count": 3, + "repos_with_open_high_critical": 2, + "total_open_high": 3, + "total_open_critical": 1 + }, + "decision": { + "decision_needed_count": 2, + "default_attention_count": 3 + } + } } \ No newline at end of file diff --git a/scripts/build_demo_artifacts.py b/scripts/build_demo_artifacts.py index 444e5f5..83d8f25 100644 --- a/scripts/build_demo_artifacts.py +++ b/scripts/build_demo_artifacts.py @@ -1,5 +1,6 @@ from __future__ import annotations +from collections import Counter import json from datetime import datetime, timezone from typing import Any @@ -101,6 +102,21 @@ def _demo_diff_data() -> dict: } +def _demo_language_trends(report_data: dict[str, Any]) -> list[dict[str, Any]]: + language_distribution = dict(report_data.get("language_distribution", {})) + return [ + { + "language": language, + "current_count": count, + "category": "Hold", + "repos_per_run": [count], + } + for language, count in sorted( + language_distribution.items(), key=lambda item: (-item[1], item[0]) + ) + ] + + def main() -> None: ( control_center_artifact_payload, @@ -119,6 +135,7 @@ def main() -> None: audit["score_explanation"] = build_score_explanation(audit) report_data["run_change_counts"] = build_run_change_counts(diff_data) report_data["run_change_summary"] = build_run_change_summary(diff_data) + report_data["language_trends"] = _demo_language_trends(report_data) report_path = OUTPUT_DIR / "demo-report.json" report_path.write_text(json.dumps(report_data, indent=2)) @@ -251,6 +268,7 @@ def _build_pcc_truth_snapshot( risk_tier = "moderate" security = _security_fields(audit) high_crit = security["dependabot_critical"] + security["dependabot_high"] + security["open_high_critical"] = high_crit attention_state = _attention_state(audit, risk_tier, high_crit) projects.append( { @@ -325,11 +343,86 @@ def _build_pcc_truth_snapshot( } ) + risk_tier_counts = Counter(project["risk"]["risk_tier"] for project in projects) + attention_state_counts = Counter( + project["derived"]["attention_state"] for project in projects + ) + context_quality_counts = Counter( + project["derived"]["context_quality"] for project in projects + ) + registry_status_counts = Counter( + project["derived"]["registry_status"] for project in projects + ) + scanned_count = sum( + 1 for project in projects if project["security"]["alerts_available"] + ) + total_open_high = sum( + project["security"]["dependabot_high"] for project in projects + ) + total_open_critical = sum( + project["security"]["dependabot_critical"] for project in projects + ) + repos_with_open_high_critical = sum( + 1 + for project in projects + if project["security"]["open_high_critical"] > 0 + ) + decision_needed_count = sum( + 1 + for project in projects + if project["derived"]["attention_state"] == "decision-needed" + ) + default_attention_count = sum( + 1 + for project in projects + if project["derived"]["attention_state"] + in {"decision-needed", "active-product", "active-infra"} + ) + return { - "schema_version": "demo-pcc-v1", + "schema_version": "0.7.0", "generated_at": generated_at, "workspace_root": "fixtures/demo", + "source_summary": { + "workspace_root": "fixtures/demo", + "project_count": len(projects), + "catalog_errors": [], + "catalog_warnings": [], + "legacy_registry_rows": len(projects), + "notion_context_rows": 0, + "context_quality_counts": dict(context_quality_counts), + "registry_status_counts": dict(registry_status_counts), + "attention_state_counts": dict(attention_state_counts), + "duplicate_display_names": [], + "unresolved_duplicate_display_names": [], + }, + "precedence_matrix": { + "identity": ["fixture"], + "declared": ["fixture"], + "derived": ["fixture report"], + "risk": ["fixture report"], + "security": ["fixture report"], + }, + "warnings": [], "projects": projects, + "rollups": { + "risk_tier_counts": { + "elevated": risk_tier_counts.get("elevated", 0), + "moderate": risk_tier_counts.get("moderate", 0), + "baseline": risk_tier_counts.get("baseline", 0), + "deferred": risk_tier_counts.get("deferred", 0), + }, + "security": { + "scanned_count": scanned_count, + "repos_with_open_high_critical": repos_with_open_high_critical, + "total_open_high": total_open_high, + "total_open_critical": total_open_critical, + }, + "decision": { + "decision_needed_count": decision_needed_count, + "default_attention_count": default_attention_count, + }, + }, } diff --git a/src/web_export.py b/src/web_export.py index 29193a7..e00c562 100644 --- a/src/web_export.py +++ b/src/web_export.py @@ -1319,7 +1319,9 @@ def _governance_section(report_data: dict) -> str: def _tech_radar_section(data: dict, trend_data: list[dict] | None) -> str: from src.history import load_language_trends - trends = load_language_trends() + trends = data.get("language_trends") + if trends is None: + trends = load_language_trends() if not trends: # Fall back to current language distribution