diff --git a/CASE-STUDY.md b/CASE-STUDY.md new file mode 100644 index 0000000..631195f --- /dev/null +++ b/CASE-STUDY.md @@ -0,0 +1,129 @@ +# Operator OS: A Multi-Agent Control Plane Over A Software Portfolio + +Operator OS is a local-first control plane for AI-assisted builders: it turns a sprawling repo portfolio and multiple coding agents into verified truth, visible risk, and one operator-approved next move. + +GitHub Repo Auditor is the truth engine behind the first public wedge. It began as a repository auditor, but the stronger product shape is a portfolio operating layer: one system that can say which projects are healthy, which are drifting, which are blocked, what changed, and what deserves attention next. + +## Who This Is For + +- Solo builders with dozens of repos and not enough trust in their own backlog. +- Staff engineers and technical leads who need decision-grade visibility across experimental, internal, and production projects. +- AI-native developers using Codex, Claude Code, ChatGPT, or similar tools across many workstreams. +- Devtools teams studying how agent-created work should be verified, prioritized, and governed. + +## The Problem + +AI coding tools make it easier to start and modify projects. They do not automatically make it easier to know what is true afterward. + +Once a portfolio has enough projects and enough agent-touched work, normal tools flatten the wrong things: + +- `git log` shows activity, not whether the work matters. +- GitHub alerts show risk, not which fix clears the most portfolio pain. +- Notes and handoffs preserve intent, but can drift from the current repo state. +- Agent transcripts are useful history, but they are not proof. +- Dashboards can look polished while hiding stale or private source data. + +Operator OS starts from a stricter premise: local evidence wins. Every product surface should be traceable back to files, commands, generated artifacts, or explicit operator approval. + +## Before And After + +| Before | After | +| --- | --- | +| A long list of repos | A portfolio truth snapshot with risk, readiness, context quality, and security posture | +| Agent work scattered across chats | Agent provenance and follow-through visible in operator surfaces | +| Weekly review rebuilt from memory | Weekly command-center artifacts generated from current audit facts | +| Security alerts handled repo by repo | Advisory-grouped burndown showing the dependency bump that clears risk across repos | +| Handoffs as stale prose | Restart-safe handoffs that say what was checked, what must be rechecked, and what not to touch | +| Automation as blind trust | Dry-run-first proposals, explicit approvals, and evidence-backed execution gates | + +## System Shape + +```mermaid +flowchart LR + A["Local repos"] --> B["GitHub Repo Auditor"] + B --> C["Portfolio truth JSON"] + B --> D["Workbook / HTML / Markdown outputs"] + C --> E["Portfolio Command Center"] + D --> E + E --> F["Operator decision"] + F --> G["Manual or gated follow-through"] +``` + +The public wedge keeps this system deliberately narrow: + +- `GithubRepoAuditor` produces portfolio truth and weekly/operator artifacts. +- `PortfolioCommandCenter` reads those artifacts and presents the operating view. +- Fixture or sanitized data drives the public demo. +- Private systems remain private implementation references, not public data sources. + +The broader local machine adds other surfaces in private use: + +- `bridge-db` for compact cross-agent receipts and state coordination. +- `personal-ops` for private inbox, planning, approvals, and local operator workflows. +- `notification-hub` for local event routing, review queues, and noise control. +- `SecondBrain` for private synthesized knowledge and source-grounded lessons. +- Codex and ChatGPT Pro workflow docs for advisory-only model review. + +Those adjacent systems are useful because they prove the operating model under real pressure. They are not required for the public demo. + +## What The Demo Shows + +The public-safe demo should show the Portfolio Command Center running over fixture or sanitized portfolio truth: + +1. A full portfolio table with risk, status, context quality, tool provenance, and security columns. +2. A risk/security tab that turns raw alert counts into portfolio-level attention. +3. A burndown tab that groups advisories by the fix that clears the most risk. +4. Trend charts that show whether risk is improving or getting worse. +5. A weekly digest that gives one headline, one decision, and one next move. + +The private local proof package for the 2026-06-07 five-tab demo lives under `docs/demo-proof/2026-06-07/`. It proves the live local demo, but it is not the public publishing package because it may reveal real local portfolio details. + +For public sharing, use the fixture-backed package under `docs/demo-proof/public-fixture/`. + +## What Stays Private + +The product should not expose raw local operating state. These surfaces are private by design: + +- Local Codex sessions, memories, reports, hooks, secrets, config, and SQLite state. +- Gmail, Calendar, Drive, task, approval, and daemon state from `personal-ops`. +- Raw SecondBrain captures, conversation exports, vault history, and private notes. +- Real Notion databases, project rows, tokens, API traces, and live write receipts. +- `bridge-db` live SQLite contents, handoffs, snapshots, receipts, recall logs, and activity rows. +- `notification-hub` events, Slack routing, local queue state, and review logs. +- Private repo names, local absolute paths, branch state, and security findings unless they are intentionally sanitized. + +The productizable asset is the pattern: local truth, bounded context, visible provenance, approval gates, and operator-facing decisions. + +## Why It Is Hard To Copy + +The moat is not a chart. Charts are easy. + +The hard part is the lived-in operating discipline: + +- One canonical truth contract feeding multiple views. +- Generated artifacts that agree with each other instead of becoming separate stories. +- Dry-run-first action flows that preserve human approval. +- Explicit stale-state handling instead of cheerful lies. +- Agent role boundaries: advisory models advise; local agents verify and execute. +- Restart-safe handoffs that force the next session back to current evidence. +- Private-by-default local operation with public-safe fixture demos. + +Most products start with a dashboard and bolt trust on later. Operator OS starts with trust and lets the dashboard expose it. + +## Public Wedge + +The first wedge is Portfolio Command Center: + +- simple enough to demo in 90 seconds; +- grounded in concrete repo facts; +- visually understandable to developers immediately; +- impressive without needing private email, calendar, Notion, or agent transcripts; +- extensible into the broader Operator OS story. + +## Demo Links + +- [90-second demo plan](DEMO-PLAN.md) +- [Fixture demo source](fixtures/demo/sample-report.json) +- [Public fixture proof package](docs/demo-proof/public-fixture/README.md) +- [Private local demo proof package](docs/demo-proof/2026-06-07/README.md) +- [Portfolio Command Center](../PortfolioCommandCenter/README.md) diff --git a/DEMO-PLAN.md b/DEMO-PLAN.md new file mode 100644 index 0000000..31b776a --- /dev/null +++ b/DEMO-PLAN.md @@ -0,0 +1,122 @@ +# Portfolio Command Center Demo Plan + +This is the public-safe demo plan for the Operator OS wedge. + +The demo should prove one thing quickly: a serious builder can turn repo sprawl and agent-touched work into verified truth, visible risk, and one next move. + +## Demo Thesis + +Git history tells you what changed. Portfolio Command Center tells you what the change means. + +## Demo Modes + +Use one of two modes: + +| Mode | Use for | Data source | Public-safe | +| --- | --- | --- | --- | +| Fixture mode | Public recording, docs, external sharing | `fixtures/demo/sample-report.json` via `make demo` | Yes | +| Live local mode | Private operator proof and internal review | `output/portfolio-truth-latest.json` from the real local portfolio | No, unless redacted | + +Default to fixture mode for anything public. + +## Fixture Demo Setup + +From this repo: + +```sh +make demo +``` + +Expected outputs: + +- `output/demo/demo-report.json` +- `output/demo/demo-workbook.xlsx` +- `output/demo/dashboard-*.html` +- `output/demo/operator-control-center-demo.json` +- `output/demo/operator-control-center-demo.md` +- `output/demo/portfolio-truth-latest.json` +- `output/demo/portfolio-warehouse.db` + +Then launch the desktop shell from the sibling app: + +```sh +cd ../PortfolioCommandCenter +pnpm install +pnpm demo:desktop +``` + +In the app header, set the output directory to: + +```text +../GithubRepoAuditor/output/demo +``` + +If the recording needs live-shaped data, create a sanitized output directory first. Do not point a public recording at the private live `output/` directory. + +## 90-Second Arc + +| Time | Frame | Spoken line | +| --- | --- | --- | +| 0:00-0:10 | Portfolio table | "This is the problem AI builders are about to have: not one repo, but a portfolio of agent-touched work." | +| 0:10-0:25 | Risk/context/status columns | "A commit timestamp is not enough. I need to know which projects are healthy, blocked, risky, stale, or worth ignoring." | +| 0:25-0:42 | Risk + Security | "The control plane turns raw alerts and project facts into an attention map, so risk stops hiding in individual repos." | +| 0:42-0:58 | Burndown | "The useful question is not just 'what is broken?' It is 'which fix clears the most portfolio pain?'" | +| 0:58-1:12 | Trends | "Because it keeps history, I can tell whether the portfolio is improving or just getting noisier." | +| 1:12-1:25 | Weekly Digest | "Every week, the system reduces the mess to one headline, one decision, and one next move." | +| 1:25-1:30 | Return to Portfolio | "That is Operator OS: verified truth for builders using agents at portfolio scale." | + +## Must-Land Product Points + +- The app is reading generated artifacts, not a hand-maintained spreadsheet. +- Portfolio truth, weekly digest, burndown, and charts come from the same evidence chain. +- The operator remains in charge. +- Public demo data is fixture-backed or sanitized. +- Private local systems are implementation references, not public data sources. + +## Do Not Show Publicly + +- Real private repo names. +- Local absolute paths under the user's home directory. +- Real GitHub security alert details. +- Notion database rows or page IDs. +- Gmail, Calendar, Drive, Slack, or task data. +- Codex sessions, memories, hook logs, or SQLite databases. +- SecondBrain raw captures or conversation exports. +- Tokens, cookies, env values, terminal scrollback, hostnames, or account settings. + +## Redaction Checklist + +Before publishing: + +- [ ] Confirm the app is using `output/demo` or another sanitized output directory. +- [ ] Confirm no private repo names are readable. +- [ ] Confirm no terminal panes or local paths are visible. +- [ ] Confirm no account names, tokens, hostnames, or private URLs are visible. +- [ ] Confirm screenshots and video frames do not expose Notion, email, calendar, Slack, or SecondBrain. +- [ ] Confirm any local live proof package is described as private/local evidence only. + +## Verification Checklist + +Run: + +```sh +make demo +python scripts/validate_proof_package.py docs/demo-proof/public-fixture/proof-package.json +``` + +For the desktop shell: + +```sh +cd ../PortfolioCommandCenter +pnpm typecheck +pnpm test +pnpm build +``` + +Visual verification is complete only after the app is opened against the fixture output and the Portfolio, Risk + Security, Burndown, Trends, and Weekly Digest tabs all render without private data. + +## Final Public Framing + +Use this closing sentence: + +> 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. diff --git a/README.md b/README.md index 605338c..573e257 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,10 @@ Treat campaign/writeback, GitHub Projects, Notion sync, catalog overrides, score - Safe demo path: run `make demo` after a local clone to generate sample artifacts from the committed fixture without a GitHub token. - Demo fixture: [fixtures/demo/sample-report.json](fixtures/demo/sample-report.json) +- Operator OS case study: [CASE-STUDY.md](CASE-STUDY.md) +- Public-safe recording plan: [DEMO-PLAN.md](DEMO-PLAN.md) +- Product brief: [docs/product/operator-os-product-brief.md](docs/product/operator-os-product-brief.md) +- Public fixture proof package: [docs/demo-proof/public-fixture/README.md](docs/demo-proof/public-fixture/README.md) - Product modes: [docs/modes.md](docs/modes.md) - Web UI operator guide: [docs/audit-serve.md](docs/audit-serve.md) - CLI migration (flat → subcommand): [docs/audit-cli-migration.md](docs/audit-cli-migration.md) @@ -192,6 +196,8 @@ pip install "github-repo-auditor[serve]" ### Try the safe demo The demo uses committed fixture data and writes only to `output/demo/`. +Use this path for public recordings and screenshots. The live local portfolio +output is private operator evidence unless it has been intentionally sanitized. ```bash git clone https://github.com/saagpatel/GithubRepoAuditor.git @@ -204,8 +210,10 @@ Expected outputs include `output/demo/demo-report.json`, `output/demo/demo-workbook.xlsx`, `output/demo/dashboard-*.html`, `output/demo/operator-control-center-demo.json`, `output/demo/operator-control-center-demo.md`, -`output/demo/portfolio-truth-latest.json`, and -`output/demo/portfolio-warehouse.db`. +`output/demo/portfolio-truth-latest.json`, +`output/demo/weekly-command-center-sample-user-2026-04-12.json`, +`output/demo/security-burndown-sample-user-2026-04-12.json`, +`output/demo/pending-proposals.json`, and `output/demo/portfolio-warehouse.db`. To browse the same fixture in the local web UI: @@ -214,6 +222,9 @@ pip install -e ".[serve]" audit serve --output-dir output/demo ``` +To record the Portfolio Command Center wedge from the same fixture, follow +[DEMO-PLAN.md](DEMO-PLAN.md) and point the desktop app at `output/demo/`. + ### Quick start (subcommand form) ```bash @@ -431,6 +442,19 @@ Common fixes: There is also a longer operator guide in [docs/operator-troubleshooting.md](docs/operator-troubleshooting.md). +## Proof Packages + +Cross-repo done-state proof now uses `proof-package.v1`; see +[docs/proof-package-contract.md](docs/proof-package-contract.md). The first +concrete package is the PortfolioCommandCenter five-tab local demo proof at +[docs/demo-proof/2026-06-07/proof-package.json](docs/demo-proof/2026-06-07/proof-package.json). + +Validate a package with: + +```bash +python scripts/validate_proof_package.py docs/demo-proof/2026-06-07/proof-package.json +``` + ## License MIT diff --git a/docs/demo-proof/2026-06-07/SUMMARY.md b/docs/demo-proof/2026-06-07/SUMMARY.md new file mode 100644 index 0000000..d37c650 --- /dev/null +++ b/docs/demo-proof/2026-06-07/SUMMARY.md @@ -0,0 +1,19 @@ +# PortfolioCommandCenter Demo Proof Package + +This package proves the 2026-06-07 local five-tab PortfolioCommandCenter demo. +GithubRepoAuditor is the evidence producer because it owns the portfolio truth +and generated screenshots; PortfolioCommandCenter is the subject repo being +demonstrated. + +Status: passed. + +Key proof points: + +- Portfolio tab: 129 projects. +- Risk + Security tab: 117 scanned repos and 63 with open high/critical alerts. +- Burndown tab: advisory-grouped fix guidance. +- Trends tab: risk and high/critical history charts. +- Weekly Digest tab: current decision ends with `Start with codexkit.` + +Use `proof-package.json` for the machine-readable claim-to-evidence map and +`README.md` for the narrative proof summary. diff --git a/docs/demo-proof/2026-06-07/proof-package.json b/docs/demo-proof/2026-06-07/proof-package.json new file mode 100644 index 0000000..2efa776 --- /dev/null +++ b/docs/demo-proof/2026-06-07/proof-package.json @@ -0,0 +1,183 @@ +{ + "schema_version": "proof-package.v1", + "package_id": "20260607-portfolio-command-center-demo", + "subject": { + "repo": "PortfolioCommandCenter", + "lane": "local-desktop-demo", + "claim": "The five-tab desktop demo reflects the current GithubRepoAuditor portfolio truth snapshot." + }, + "producer": { + "repo": "GithubRepoAuditor", + "mode": "demo", + "commands": [ + "python -m src.cli --portfolio-truth --portfolio-truth-include-security saagpatel", + "python -m src.cli triage saagpatel --control-center", + "pnpm demo:desktop" + ] + }, + "source_state": { + "generated_at": "2026-06-07T17:00:59.463918+00:00", + "git_branch": "main", + "git_status": "dirty-preexisting-delete:.github/pull_request_template.md", + "freshness_window_hours": 24, + "source_truth_schema": "0.5.0" + }, + "claims": [ + { + "id": "portfolio-tab-count", + "statement": "The Portfolio tab shows 129 projects from the truth snapshot.", + "status": "passed", + "evidence": [ + "portfolio-screenshot", + "portfolio-truth" + ] + }, + { + "id": "risk-security-tab", + "statement": "The Risk + Security tab shows 117 scanned repos and 63 repos with open high/critical Dependabot alerts.", + "status": "passed", + "evidence": [ + "risk-security-screenshot", + "portfolio-truth" + ] + }, + { + "id": "burndown-tab", + "statement": "The Burndown tab renders advisory-grouped fix guidance with affected repo counts.", + "status": "passed", + "evidence": [ + "burndown-screenshot" + ] + }, + { + "id": "trends-tab", + "statement": "The Trends tab renders risk-tier and high/critical history charts.", + "status": "passed", + "evidence": [ + "trends-screenshot" + ] + }, + { + "id": "weekly-digest-tab", + "statement": "The Weekly Digest tab shows the current decision ending with 'Start with codexkit.'", + "status": "passed", + "evidence": [ + "weekly-digest-screenshot", + "weekly-digest-json" + ] + }, + { + "id": "five-frame-contact-sheet", + "statement": "A contact sheet exists for quick visual smoke review of all five frames.", + "status": "passed", + "evidence": [ + "contact-sheet" + ] + } + ], + "verification": { + "overall": "passed", + "checks": [ + { + "name": "manifest references local screenshots and source receipts", + "status": "passed" + }, + { + "name": "demo proof README lists commands, source truth, and proof points", + "status": "passed" + } + ], + "missing_receipts": [], + "known_gaps": [ + "This package proves the 2026-06-07 local demo capture, not current live runtime freshness after that timestamp.", + "PortfolioCommandCenter is the subject repo; screenshot evidence is intentionally stored under the GithubRepoAuditor producer repo." + ] + }, + "safety": { + "redaction": "none", + "secrets_checked": true, + "live_write_performed": false + }, + "artifacts": [ + { + "id": "demo-readme", + "kind": "summary", + "path": "README.md", + "description": "Human-readable demo proof summary.", + "required": true + }, + { + "id": "recording-checklist", + "kind": "checklist", + "path": "RECORDING-CHECKLIST.md", + "description": "90-second capture order and publish-time checks.", + "required": true + }, + { + "id": "portfolio-screenshot", + "kind": "screenshot", + "path": "images/01-portfolio.png", + "description": "Portfolio tab screenshot.", + "required": true + }, + { + "id": "risk-security-screenshot", + "kind": "screenshot", + "path": "images/02-risk-security.png", + "description": "Risk + Security tab screenshot.", + "required": true + }, + { + "id": "burndown-screenshot", + "kind": "screenshot", + "path": "images/03-burndown.png", + "description": "Burndown tab screenshot.", + "required": true + }, + { + "id": "trends-screenshot", + "kind": "screenshot", + "path": "images/04-trends.png", + "description": "Trends tab screenshot.", + "required": true + }, + { + "id": "weekly-digest-screenshot", + "kind": "screenshot", + "path": "images/05-weekly-digest.png", + "description": "Weekly Digest tab screenshot.", + "required": true + }, + { + "id": "contact-sheet", + "kind": "screenshot", + "path": "images/contact-sheet.png", + "description": "Five-frame visual smoke contact sheet.", + "required": true + }, + { + "id": "portfolio-truth", + "kind": "json-receipt", + "path": "../../../output/portfolio-truth-latest.json", + "description": "Canonical portfolio truth snapshot consumed by the demo.", + "required": true, + "owner_repo": "GithubRepoAuditor" + }, + { + "id": "weekly-digest-json", + "kind": "json-receipt", + "path": "../../../output/weekly-command-center-saagpatel-2026-06-03.json", + "description": "Weekly command-center digest consumed by the Weekly Digest tab.", + "required": true, + "owner_repo": "GithubRepoAuditor" + }, + { + "id": "portfolio-command-center-readme", + "kind": "repo-doc", + "path": "../../../../PortfolioCommandCenter/README.md", + "description": "Subject repo demo instructions and proof pointer.", + "required": true, + "owner_repo": "PortfolioCommandCenter" + } + ] +} diff --git a/docs/demo-proof/public-fixture/README.md b/docs/demo-proof/public-fixture/README.md new file mode 100644 index 0000000..ac3fec0 --- /dev/null +++ b/docs/demo-proof/public-fixture/README.md @@ -0,0 +1,51 @@ +# Public Fixture Demo Proof + +This proof package covers the public-safe Portfolio Command Center demo path. + +Unlike the private local proof package in `../2026-06-07/`, this package is +designed for external sharing. It uses committed fixture data and generated demo +artifacts under `output/demo/`, so it does not require private repo state, +tokens, Notion, bridge-db, personal-ops, SecondBrain, or notification-hub. + +## Generate The Evidence + +From the GitHub Repo Auditor repo: + +```sh +make demo +python scripts/validate_proof_package.py docs/demo-proof/public-fixture/proof-package.json +``` + +Expected generated artifacts: + +- `output/demo/demo-report.json` +- `output/demo/demo-workbook.xlsx` +- `output/demo/dashboard-sample-user-2026-04-12.html` +- `output/demo/operator-control-center-demo.json` +- `output/demo/operator-control-center-demo.md` +- `output/demo/portfolio-truth-latest.json` +- `output/demo/weekly-command-center-sample-user-2026-04-12.json` +- `output/demo/security-burndown-sample-user-2026-04-12.json` +- `output/demo/pending-proposals.json` +- `output/demo/portfolio-warehouse.db` + +## Desktop Demo + +From the sibling Portfolio Command Center repo: + +```sh +pnpm install +pnpm demo:desktop +``` + +Then set the output directory in the app header to: + +```text +../GithubRepoAuditor/output/demo +``` + +## 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. diff --git a/docs/demo-proof/public-fixture/RECORDING-CHECKLIST.md b/docs/demo-proof/public-fixture/RECORDING-CHECKLIST.md new file mode 100644 index 0000000..eac5cbf --- /dev/null +++ b/docs/demo-proof/public-fixture/RECORDING-CHECKLIST.md @@ -0,0 +1,32 @@ +# Public Fixture Recording Checklist + +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`. +- [ ] Confirm the visible data is fixture data, not the private live portfolio. +- [ ] Hide terminals, path bars, desktop clutter, account menus, and notification banners. + +## Shot Order + +| Time | Tab | What to show | +| --- | --- | --- | +| 0:00-0:10 | Portfolio | The table and portfolio summary. | +| 0:10-0:25 | Portfolio | Risk, status, context, and tool/provenance columns. | +| 0:25-0:42 | Risk + Security | Portfolio-level risk and security posture. | +| 0:42-0:58 | Burndown | Advisory-grouped fix guidance. | +| 0:58-1:12 | Trends | Risk and alert history. | +| 1:12-1:25 | Weekly Digest | One headline, one decision, one next move. | +| 1:25-1:30 | Portfolio | Close on the Operator OS thesis. | + +## Do Not Publish If Visible + +- private repo names; +- local absolute paths; +- hostnames, usernames, or account menus; +- real security advisory details; +- Notion, email, calendar, Slack, bridge-db, or SecondBrain content; +- terminal scrollback, env vars, tokens, cookies, or config files. diff --git a/docs/demo-proof/public-fixture/SUMMARY.md b/docs/demo-proof/public-fixture/SUMMARY.md new file mode 100644 index 0000000..d39f5b5 --- /dev/null +++ b/docs/demo-proof/public-fixture/SUMMARY.md @@ -0,0 +1,18 @@ +# Public Fixture Demo Summary + +Status: fixture proof package, pending 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; +- 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. diff --git a/docs/demo-proof/public-fixture/proof-package.json b/docs/demo-proof/public-fixture/proof-package.json new file mode 100644 index 0000000..09c60d2 --- /dev/null +++ b/docs/demo-proof/public-fixture/proof-package.json @@ -0,0 +1,227 @@ +{ + "schema_version": "proof-package.v1", + "package_id": "public-fixture-portfolio-command-center-demo", + "subject": { + "repo": "PortfolioCommandCenter", + "lane": "public-fixture-demo", + "claim": "Portfolio Command Center can be demonstrated from fixture-backed GitHub Repo Auditor artifacts without private local operating data." + }, + "producer": { + "repo": "GithubRepoAuditor", + "mode": "fixture", + "commands": [ + "make demo", + "python scripts/validate_proof_package.py docs/demo-proof/public-fixture/proof-package.json", + "pnpm demo:desktop" + ] + }, + "source_state": { + "source_data_mode": "fixture", + "fixture": "../../../fixtures/demo/sample-report.json", + "generated_output_dir": "../../../output/demo", + "source_truth_schema": "demo-pcc-v1", + "freshness_window_hours": null + }, + "claims": [ + { + "id": "fixture-input", + "statement": "The public demo source is a committed fixture file, not the private live portfolio output.", + "status": "passed", + "evidence": [ + "fixture-report" + ] + }, + { + "id": "demo-generation", + "statement": "The fixture demo command generates the JSON, workbook, dashboard, control-center, truth, and warehouse artifacts under output/demo.", + "status": "passed", + "evidence": [ + "demo-make-target", + "demo-build-script", + "demo-report", + "demo-workbook", + "demo-dashboard", + "demo-control-center-json", + "demo-control-center-markdown", + "demo-portfolio-truth", + "demo-weekly-digest", + "demo-security-burndown", + "demo-history-previous", + "demo-history-current", + "demo-proposals", + "demo-warehouse" + ] + }, + { + "id": "desktop-compatible-schema", + "statement": "The generated portfolio truth uses the PortfolioCommandCenter projects schema rather than the older lightweight repos demo shape.", + "status": "passed", + "evidence": [ + "demo-portfolio-truth", + "demo-weekly-digest", + "demo-security-burndown" + ] + }, + { + "id": "public-recording-boundary", + "statement": "The public recording checklist requires fixture output and blocks private local data exposure.", + "status": "passed", + "evidence": [ + "recording-checklist" + ] + }, + { + "id": "visual-capture", + "statement": "Final public screenshots or video frames still need a frame-level privacy review after capture.", + "status": "partial", + "evidence": [ + "summary" + ] + } + ], + "verification": { + "overall": "partial", + "checks": [ + { + "name": "manifest references fixture input and generated output paths", + "status": "passed" + }, + { + "name": "visual capture from Portfolio Command Center", + "status": "partial" + } + ], + "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." + ] + }, + "safety": { + "redaction": "fixture-only", + "secrets_checked": true, + "live_write_performed": false, + "private_data_required": false + }, + "artifacts": [ + { + "id": "summary", + "kind": "summary", + "path": "SUMMARY.md", + "description": "Human-readable public fixture demo summary.", + "required": true + }, + { + "id": "recording-checklist", + "kind": "checklist", + "path": "RECORDING-CHECKLIST.md", + "description": "Public-safe recording checklist and redaction guard.", + "required": true + }, + { + "id": "fixture-report", + "kind": "fixture", + "path": "../../../fixtures/demo/sample-report.json", + "description": "Committed fixture report used as the public demo source.", + "required": true + }, + { + "id": "demo-make-target", + "kind": "repo-doc", + "path": "../../../Makefile", + "description": "Make target that runs the fixture demo generator.", + "required": true + }, + { + "id": "demo-build-script", + "kind": "script", + "path": "../../../scripts/build_demo_artifacts.py", + "description": "Fixture artifact generator for output/demo.", + "required": true + }, + { + "id": "demo-report", + "kind": "json", + "path": "../../../output/demo/demo-report.json", + "description": "Generated demo report.", + "required": true + }, + { + "id": "demo-workbook", + "kind": "workbook", + "path": "../../../output/demo/demo-workbook.xlsx", + "description": "Generated demo workbook.", + "required": true + }, + { + "id": "demo-dashboard", + "kind": "html", + "path": "../../../output/demo/dashboard-sample-user-2026-04-12.html", + "description": "Generated demo HTML dashboard.", + "required": true + }, + { + "id": "demo-control-center-json", + "kind": "json", + "path": "../../../output/demo/operator-control-center-demo.json", + "description": "Generated demo operator control-center JSON.", + "required": true + }, + { + "id": "demo-control-center-markdown", + "kind": "markdown", + "path": "../../../output/demo/operator-control-center-demo.md", + "description": "Generated demo operator control-center Markdown.", + "required": true + }, + { + "id": "demo-portfolio-truth", + "kind": "json", + "path": "../../../output/demo/portfolio-truth-latest.json", + "description": "Generated demo portfolio truth snapshot.", + "required": true + }, + { + "id": "demo-weekly-digest", + "kind": "json", + "path": "../../../output/demo/weekly-command-center-sample-user-2026-04-12.json", + "description": "Generated demo weekly command-center digest.", + "required": true + }, + { + "id": "demo-security-burndown", + "kind": "json", + "path": "../../../output/demo/security-burndown-sample-user-2026-04-12.json", + "description": "Generated demo security burndown report.", + "required": true + }, + { + "id": "demo-history-previous", + "kind": "json", + "path": "../../../output/demo/portfolio-truth-2026-04-05T120000Z.json", + "description": "Generated previous demo truth snapshot for trends.", + "required": true + }, + { + "id": "demo-history-current", + "kind": "json", + "path": "../../../output/demo/portfolio-truth-2026-04-12T120000Z.json", + "description": "Generated current demo truth snapshot for trends.", + "required": true + }, + { + "id": "demo-proposals", + "kind": "json", + "path": "../../../output/demo/pending-proposals.json", + "description": "Generated empty automation proposal queue.", + "required": true + }, + { + "id": "demo-warehouse", + "kind": "sqlite", + "path": "../../../output/demo/portfolio-warehouse.db", + "description": "Generated demo warehouse snapshot.", + "required": true + } + ] +} diff --git a/docs/product/operator-os-product-brief.md b/docs/product/operator-os-product-brief.md new file mode 100644 index 0000000..b6fad41 --- /dev/null +++ b/docs/product/operator-os-product-brief.md @@ -0,0 +1,97 @@ +# Operator OS Product Brief + +## Strongest Narrative + +Operator OS is a local-first control plane for AI-assisted builders. + +It turns a sprawling repo portfolio and multiple coding agents into verified truth, visible risk, and one operator-approved next move. + +## Product Wedge + +The first public wedge is Portfolio Command Center, a desktop command center over GitHub Repo Auditor's portfolio truth snapshot. + +This wedge is strong because it is concrete: + +- developers understand repo sprawl immediately; +- the data can be fixture-backed for public demos; +- the value is visible in under 90 seconds; +- the trust model can be explained without exposing private personal systems. + +## Target Users + +- AI-native solo builders with many active and abandoned repos. +- Staff engineers coordinating experiments, internal tools, and production-adjacent prototypes. +- Engineering leaders who need a decision-ready view of project risk and readiness. +- Devtools teams exploring human-supervised agent workflows. + +## Jobs To Be Done + +- "Tell me which projects are worth attention this week." +- "Show me where risk is building before it becomes an incident." +- "Help me know whether agent-created work is actually verified." +- "Give me one next move without hiding the evidence." +- "Let me use AI agents without giving them silent authority over real systems." + +## Product Pillars + +1. **Truth before advice** + Generated artifacts and local checks outrank memory, transcripts, and summaries. + +2. **Operator-approved movement** + Risky actions stay dry-run-first, reviewable, and explicitly approved. + +3. **Private by default** + Local operating data stays local. Public demos use fixtures or sanitized data. + +4. **Multi-agent clarity** + Advisory models advise. Local execution agents verify. The operator decides. + +5. **One next move** + The interface should reduce noise into a decision, not produce another dashboard to babysit. + +## What Is Uniquely Hard To Copy + +- A canonical portfolio truth contract feeding JSON, Markdown, workbook, HTML, and desktop surfaces. +- Follow-through state that tracks whether recommendations were attempted, stale, recovering, or retired. +- Explicit stale-state handling and restart-safe handoffs. +- Dry-run-first approval gates across repo, Notion, and workflow surfaces. +- Local/private operation with public-safe proof packages. +- Real usage across Codex, Claude Code, ChatGPT Pro, bridge state, notifications, and personal operations without blurring ownership. + +## Too Private To Productize Directly + +- Personal email, calendar, Drive, task, approval, and daemon state. +- Raw SecondBrain captures and conversation exports. +- Live bridge-db SQLite state, handoffs, recall logs, and shipped-sync receipts. +- Notification logs, Slack routing, and local queue state. +- Notion database rows, page IDs, tokens, and live write receipts. +- Codex sessions, secrets, memories, hook state, and machine-local SQLite databases. +- Real private repo security posture unless explicitly sanitized. + +## Public Demo + +Use fixture-backed Portfolio Command Center: + +1. Generate fixture artifacts with `make demo`. +2. Open Portfolio Command Center against `output/demo`. +3. Show five frames: Portfolio, Risk + Security, Burndown, Trends, Weekly Digest. +4. Close on the Operator OS thesis. + +The public demo should not require GitHub tokens, Notion tokens, bridge-db, personal-ops, SecondBrain, notification-hub, or local private logs. + +## MVP Product Package + +- `CASE-STUDY.md`: the public case study. +- `DEMO-PLAN.md`: the recording and safety plan. +- `docs/demo-proof/public-fixture/`: machine-readable proof package for public demo safety. +- PortfolioCommandCenter README: live mode and public fixture mode clearly separated. +- GitHub Repo Auditor fixture data: repeatable public artifact generation. + +## Future Directions + +- A hosted static fixture demo that never touches local data. +- A sanitized sample portfolio generator. +- A web-first viewer for the same portfolio truth contract. +- A plugin model for organization-specific analyzers. +- An explicit "agent provenance" schema for which agent touched which repo and with what verification. +- A trust dashboard for dry runs, approvals, and follow-through outcomes. diff --git a/docs/proof-package-contract.md b/docs/proof-package-contract.md new file mode 100644 index 0000000..0b318da --- /dev/null +++ b/docs/proof-package-contract.md @@ -0,0 +1,100 @@ +# Proof Package Contract v1 + +Proof packages are small, durable evidence bundles for done-state claims. They do +not replace each repo's native reports, screenshots, release assets, dry-run +outputs, or health commands. They point at those artifacts and make the claim +auditable from one manifest. + +Use a proof package whenever future work needs to prove that a demo, live write, +dry run, release, sync, health gate, or operational burn-in is actually done. + +## Layout + +```text +docs/proof-packages/-/ + proof-package.json + SUMMARY.md + evidence/ + receipts/ + logs/ +``` + +The package may reference existing artifacts outside the package when copying +them would create churn. Cross-repo references are allowed, but the manifest must +name the producer, subject, and evidence owner explicitly. + +## Manifest + +`proof-package.json` is required. + +Required top-level fields: + +- `schema_version`: must be `proof-package.v1`. +- `package_id`: stable package identifier. +- `subject`: repo, lane, and claim being proven. +- `producer`: repo or command surface that produced the evidence. +- `source_state`: generation time, branch/status when known, and freshness. +- `claims`: list of specific statements with status and evidence. +- `verification`: overall result, checks, missing receipts, and known gaps. +- `safety`: live-write and redaction posture. +- `artifacts`: all referenced files or external evidence. + +Allowed claim and verification statuses: + +- `passed` +- `failed` +- `partial` +- `stale` + +## Artifact Rules + +Each artifact entry should include: + +- `id` +- `kind` +- `path` +- `description` +- `required` + +Optional artifact fields: + +- `external`: set to `true` when the path is outside the manifest directory and + should not be checked by the lightweight validator. +- `owner_repo`: useful for cross-repo proof, such as PortfolioCommandCenter proof + stored under GithubRepoAuditor. + +Local relative artifacts are validated relative to the manifest file. + +## Claim Rules + +Every claim needs: + +- `id` +- `statement` +- `status` +- `evidence` + +`evidence` is a list of artifact IDs. A claim may also include `notes` when the +proof is intentionally bounded or stale-prone. + +## Done-State Rules + +- Demo proof requires source truth plus screenshots or recording. +- Dry-run proof requires planned writes, failed/partial steps, and a live + go/no-go decision. +- Live-write proof requires read-back or downstream receipt. +- Runtime proof requires a health, burn-in, or status output with explicit + failure/drift fields. +- Release proof requires build/test/install or checksum evidence. + +## Validation + +Run: + +```bash +python scripts/validate_proof_package.py docs/proof-packages//proof-package.json +``` + +The validator checks structure and local file references. It intentionally does +not judge whether a claim is true; the package author must still choose honest +claim statements and bounded evidence. diff --git a/docs/weekly-review.md b/docs/weekly-review.md index 0870b5f..9e25f1f 100644 --- a/docs/weekly-review.md +++ b/docs/weekly-review.md @@ -21,7 +21,7 @@ Use this guide for the normal ongoing operator loop. If you need the broader pro The primary workbook, HTML, Markdown, review-pack, and scheduled-handoff surfaces all tell the same compressed operator story: - `Act Now` means blocked or urgent pressure is active right now. -- `Watch Closely` means the repo is active and deserves attention, but it does not outrank the highest-pressure work yet. +- `Watch Closely` means the repo has an active attention state or concrete decision signal, but it does not outrank the highest-pressure work yet. - `Improving` means the path is stabilizing and recent action is helping. - `Fragile` means progress is real but easy to lose. - `Revalidate` means confidence still needs to be rebuilt before the repo should be treated as restored. diff --git a/output/demo/dashboard-sample-user-2026-04-12.html b/output/demo/dashboard-sample-user-2026-04-12.html new file mode 100644 index 0000000..b46a418 --- /dev/null +++ b/output/demo/dashboard-sample-user-2026-04-12.html @@ -0,0 +1,864 @@ + + + + +Portfolio Dashboard: sample-user + + + + + +
+

Portfolio Dashboard: sample-user

+

Generated 2026-04-12 | 3 repos audited | Grade B

+
+ +
+
Avg Score
0.65
+
Shipped
1
+
Functional
1
+
Needs Work
0
+
+ +
+

Risk Posture

+
+
Elevated
+
2
+
Moderate
+
0
+
Baseline
+
1
+
Deferred
+
0
+
+
  • RepoB: Security follow-through needs operator review.
  • RepoC: Security follow-through needs operator review.
+
+ + +
+

Operator Control Center

+
+
Headline: RepoC still needs security follow-through before the queue will calm down.
+
Run Changes: Average score moved +0.020; 0 meaningful improvements, 0 regressions, 1 promotions, and 1 demotions were recorded.
+
Queue Pressure: 1 blocked, 1 urgent, 1 ready, and 1 deferred item(s) are currently in the queue.
+
Trust / Actionability: verify-first — The recommendation is sound, but recent reopen noise means it should still be reviewed before acting.
+
Top Recommendation: Review RepoC first, then close RepoB's reopened checklist item.
+
Source Run: sample-user:2026-04-12T12:00:00+00:00
+
Next Recommended Run: incremental
+
Watch Strategy: adaptive
+
Watch Decision: The current baseline is still compatible, so incremental watch remains safe for the next run.
+
What Changed: RepoC drift needs review and RepoB reopened a release checklist item.
+
Why It Matters: Live drift is still present, so security work should stay ahead of lower-pressure cleanup.
+
What To Do Next: Review RepoC first, then close RepoB's reopened checklist item.
+
Trend: Queue pressure is stable but still sticky.
+
Accountability: No accountability summary is recorded yet.
+
Follow-Through: One urgent item is still repeating in the recent window.
+
Next Checkpoint: Use the next run or linked artifact to confirm whether the recommendation moved.
+
Escalation: No stronger follow-through escalation is currently surfaced.
+
Recovery / Retirement: No follow-through recovery or escalation-retirement signal is currently surfaced.
+
Recovery Persistence: No follow-through recovery persistence signal is currently surfaced.
+
Relapse Churn: No relapse churn is currently surfaced.
+
Recovery Freshness: No follow-through recovery freshness signal is currently surfaced.
+
Recovery Memory Reset: No follow-through recovery memory reset signal is currently surfaced.
+
Recovery Rebuild Strength: No follow-through recovery rebuild-strength signal is currently surfaced.
+
Recovery Reacquisition: No follow-through recovery reacquisition signal is currently surfaced.
+
Reacquisition Durability: No follow-through reacquisition durability signal is currently surfaced.
+
Reacquisition Confidence: No follow-through reacquisition confidence-consolidation signal is currently surfaced.
+
Reacquisition Softening Decay: No reacquisition softening-decay signal is currently surfaced.
+
Reacquisition Confidence Retirement: No reacquisition confidence-retirement signal is currently surfaced.
+
Revalidation Recovery: No post-revalidation recovery or confidence re-earning signal is currently surfaced.
+
Primary Target: No active target
+
Why This Is The Top Target: RepoC remains the top target because live drift is still open.
+
What Counts As Done: No done-state guidance is recorded yet.
+
Closure Guidance: Clear RepoC's security drift before moving to lower-pressure work.
+
What We Tried: No intervention evidence is recorded yet.
+
Resolution Evidence: No resolution evidence is recorded yet.
+
Primary Target Confidence: low (0.00)
+
Confidence Reasons: No confidence rationale is recorded yet.
+
Next Action Confidence: low (0.00)
+
Trust Policy: verify-first — The recommendation is sound, but recent reopen noise means it should still be reviewed before acting.
+
Why This Confidence Is Actionable: No adaptive confidence summary is recorded yet.
+
Trust Policy Exception: none — No trust-policy exception reason is recorded yet.
+
Exception Pattern Learning: none — No exception-pattern reason is recorded yet.
+
Trust Recovery: none — No trust-recovery reason is recorded yet.
+
Recovery Confidence: low (0.00) — No recovery-confidence summary is recorded yet.
+
Exception Retirement: none — No exception-retirement reason is recorded yet.
+
Policy Debt Cleanup: none — No policy-debt reason is recorded yet.
+
Class-Level Trust Normalization: none — No class-normalization reason is recorded yet.
+
Class Memory Freshness: insufficient-data — No class-memory freshness reason is recorded yet.
+
Trust Decay Controls: none — No class-decay reason is recorded yet.
+
Class Trust Reweighting: neutral (0.00)
+
Why Class Guidance Shifted: No class reweighting rationale is recorded yet.
+
Class Trust Momentum: insufficient-data (0.00)
+
Reweighting Stability: watch — none: No class transition reason is recorded yet.
+
Class Transition Health: none — No class transition health reason is recorded yet.
+
Pending Transition Resolution: none — No class transition resolution reason is recorded yet.
+
Transition Closure Confidence: low (0.00) — none
+
Class Pending Debt Audit: none — No class pending-debt reason is recorded yet.
+
Pending Debt Freshness: insufficient-data — No pending-debt freshness reason is recorded yet.
+
Closure Forecast Reweighting: neutral (0.00)
+
Closure Forecast Momentum: insufficient-data (0.00)
+
Closure Forecast Freshness: insufficient-data — No closure-forecast freshness reason is recorded yet.
+
Closure Forecast Hysteresis: watch — none: No closure-forecast hysteresis reason is recorded yet.
+
Hysteresis Decay Controls: none — No closure-forecast decay reason is recorded yet.
+
Closure Forecast Refresh Recovery: none (0.00)
+
Reacquisition Controls: none — No closure-forecast reacquisition reason is recorded yet.
+
Reacquisition Persistence: none (0.00; 0 run(s))
+
Recovery Churn Controls: none — No recovery-churn reason is recorded yet.
+
Reacquisition Freshness: insufficient-data — No reacquisition-freshness reason is recorded yet.
+
Persistence Reset Controls: none — No persistence-reset reason is recorded yet.
+
Reset Refresh Recovery: none (0.00)
+
Reset Re-entry Controls: none — No reset re-entry reason is recorded yet.
+
Reset Re-entry Persistence: none (0.00; 0 run(s))
+
Reset Re-entry Churn Controls: none — No reset re-entry churn reason is recorded yet.
+
Reset Re-entry Freshness: insufficient-data — No reset re-entry freshness reason is recorded yet.
+
Reset Re-entry Reset Controls: none — No reset re-entry reset reason is recorded yet.
+
Reset Re-entry Refresh Recovery: none (0.00)
+
Reset Re-entry Rebuild Controls: none — No reset re-entry rebuild reason is recorded yet.
+
Reset Re-entry Rebuild Freshness: insufficient-data — No reset re-entry rebuild freshness reason is recorded yet.
+
Reset Re-entry Rebuild Reset Controls: none — No reset re-entry rebuild reset reason is recorded yet.
+
Reset Re-entry Rebuild Refresh Recovery: none (0.00)
+
Reset Re-entry Rebuild Re-entry Controls: none — No reset re-entry rebuild re-entry reason is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Persistence: none (0.00; 0 run(s))
+
Reset Re-entry Rebuild Re-Entry Churn Controls: none — No reset re-entry rebuild re-entry churn reason is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Freshness: insufficient-data — No reset re-entry rebuild re-entry freshness reason is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Reset Controls: none — No reset re-entry rebuild re-entry reset reason is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Refresh Recovery: none — No reset re-entry rebuild re-entry refresh recovery summary is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Controls: none — No reset re-entry rebuild re-entry restore reason is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Freshness: insufficient-data — No reset re-entry rebuild re-entry restore freshness reason is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Reset Controls: none — No reset re-entry rebuild re-entry restore reset reason is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Refresh Recovery: none — No reset re-entry rebuild re-entry restore refresh recovery summary is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Re-Restore Controls: none — No reset re-entry rebuild re-entry restore re-restore reason is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Re-Restore Persistence: none (0.00; 0 run(s))
+
Reset Re-entry Rebuild Re-Entry Restore Re-Restore Churn Controls: none — No reset re-entry rebuild re-entry restore re-restore churn reason is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Re-Restore Freshness: insufficient-data — No reset re-entry rebuild re-entry restore re-restore freshness reason is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Re-Restore Reset Controls: none — No reset re-entry rebuild re-entry restore re-restore reset reason is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Re-Restore Refresh Recovery: none — No reset re-entry rebuild re-entry restore re-restore refresh recovery summary is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Re-Re-Restore Controls: none — No reset re-entry rebuild re-entry restore re-re-restore reason is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Re-Re-Restore Persistence: none (0.00; 0 run(s))
+
Reset Re-entry Rebuild Re-Entry Restore Re-Re-Restore Churn Controls: none — No reset re-entry rebuild re-entry restore re-re-restore churn reason is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Re-Re-Restore Freshness: insufficient-data — No reset re-entry rebuild re-entry restore re-re-restore freshness reason is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Re-Re-Restore Reset Controls: none — No reset re-entry rebuild re-entry restore re-re-restore reset reason is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Re-Re-Restore Refresh Recovery: none — No reset re-entry rebuild re-entry restore re-re-restore refresh recovery summary is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Re-Re-Re-Restore Controls: none — No reset re-entry rebuild re-entry restore re-re-re-restore reason is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Re-Re-Re-Restore Persistence: none (0.00; 0 run(s))
+
Reset Re-entry Rebuild Re-Entry Restore Re-Re-Re-Restore Churn Controls: none — No reset re-entry rebuild re-entry restore re-re-re-restore churn reason is recorded yet.
+
Reset Re-entry Rebuild Persistence: none (0.00; 0 run(s))
+
Reset Re-entry Rebuild Churn Controls: none — No reset re-entry rebuild churn reason is recorded yet.
+
Recommendation Drift: stable — No recommendation-drift summary is recorded yet.
+
Exception Pattern Summary: No exception-pattern summary is recorded yet.
+
Exception Retirement Summary: No exception-retirement summary is recorded yet.
+
Policy Debt Summary: No policy-debt summary is recorded yet.
+
Trust Normalization Summary: No trust-normalization summary is recorded yet.
+
Class Memory Summary: No class-memory summary is recorded yet.
+
Class Decay Summary: No class-decay summary is recorded yet.
+
Class Reweighting Summary: No class reweighting summary is recorded yet.
+
Class Momentum Summary: No class momentum summary is recorded yet.
+
Reweighting Stability Summary: No reweighting stability summary is recorded yet.
+
Class Transition Health Summary: No class transition health summary is recorded yet.
+
Pending Transition Resolution Summary: No class transition resolution summary is recorded yet.
+
Transition Closure Confidence Summary: No transition-closure confidence summary is recorded yet.
+
Class Pending Debt Summary: No class pending-debt summary is recorded yet.
+
Class Pending Resolution Summary: No class pending-resolution summary is recorded yet.
+
Pending Debt Freshness Summary: No pending-debt freshness summary is recorded yet.
+
Pending Debt Decay Summary: No pending-debt decay summary is recorded yet.
+
Closure Forecast Reweighting Summary: No closure-forecast reweighting summary is recorded yet.
+
Closure Forecast Momentum Summary: No closure-forecast momentum summary is recorded yet.
+
Closure Forecast Freshness Summary: No closure-forecast freshness summary is recorded yet.
+
Closure Forecast Stability Summary: No closure-forecast stability summary is recorded yet.
+
Closure Forecast Hysteresis Summary: No closure-forecast hysteresis summary is recorded yet.
+
Closure Forecast Decay Summary: No closure-forecast decay summary is recorded yet.
+
Closure Forecast Refresh Recovery Summary: No closure-forecast refresh-recovery summary is recorded yet.
+
Closure Forecast Reacquisition Summary: No closure-forecast reacquisition summary is recorded yet.
+
Reacquisition Persistence Summary: No reacquisition-persistence summary is recorded yet.
+
Recovery Churn Summary: No recovery-churn summary is recorded yet.
+
Reacquisition Freshness Summary: No reacquisition-freshness summary is recorded yet.
+
Persistence Reset Summary: No persistence-reset summary is recorded yet.
+
Reset Refresh Recovery Summary: No reset-refresh recovery summary is recorded yet.
+
Reset Re-entry Summary: No reset re-entry summary is recorded yet.
+
Reset Re-entry Persistence Summary: No reset re-entry persistence summary is recorded yet.
+
Reset Re-entry Churn Summary: No reset re-entry churn summary is recorded yet.
+
Reset Re-entry Freshness Summary: No reset re-entry freshness summary is recorded yet.
+
Reset Re-entry Reset Summary: No reset re-entry reset summary is recorded yet.
+
Reset Re-entry Refresh Recovery Summary: No reset re-entry refresh-recovery summary is recorded yet.
+
Reset Re-entry Rebuild Summary: No reset re-entry rebuild summary is recorded yet.
+
Reset Re-entry Rebuild Freshness Summary: No reset re-entry rebuild-freshness summary is recorded yet.
+
Reset Re-entry Rebuild Reset Summary: No reset re-entry rebuild-reset summary is recorded yet.
+
Reset Re-entry Rebuild Refresh Recovery Summary: No reset re-entry rebuild-refresh-recovery summary is recorded yet.
+
Reset Re-entry Rebuild Re-entry Summary: No reset re-entry rebuild re-entry summary is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Persistence Summary: No reset re-entry rebuild re-entry persistence summary is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Churn Summary: No reset re-entry rebuild re-entry churn summary is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Freshness Summary: No reset re-entry rebuild re-entry freshness summary is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Reset Summary: No reset re-entry rebuild re-entry reset summary is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Refresh Recovery Summary: No reset re-entry rebuild re-entry refresh recovery summary is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Summary: No reset re-entry rebuild re-entry restore summary is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Freshness Summary: No reset re-entry rebuild re-entry restore freshness summary is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Reset Summary: No reset re-entry rebuild re-entry restore reset summary is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Refresh Recovery Summary: No reset re-entry rebuild re-entry restore refresh recovery summary is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Re-Restore Summary: No reset re-entry rebuild re-entry restore re-restore summary is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Re-Restore Persistence Summary: No reset re-entry rebuild re-entry restore re-restore persistence summary is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Re-Restore Churn Summary: No reset re-entry rebuild re-entry restore re-restore churn summary is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Re-Restore Freshness Summary: No reset re-entry rebuild re-entry restore re-restore freshness summary is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Re-Restore Reset Summary: No reset re-entry rebuild re-entry restore re-restore reset summary is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Re-Restore Refresh Recovery Summary: No reset re-entry rebuild re-entry restore re-restore refresh recovery summary is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Re-Re-Restore Summary: No reset re-entry rebuild re-entry restore re-re-restore summary is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Re-Re-Restore Persistence Summary: No reset re-entry rebuild re-entry restore re-re-restore persistence summary is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Re-Re-Restore Churn Summary: No reset re-entry rebuild re-entry restore re-re-restore churn summary is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Re-Re-Restore Freshness Summary: No reset re-entry rebuild re-entry restore re-re-restore freshness summary is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Re-Re-Restore Reset Summary: No reset re-entry rebuild re-entry restore re-re-restore reset summary is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Re-Re-Restore Refresh Recovery Summary: No reset re-entry rebuild re-entry restore re-re-restore refresh recovery summary is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Re-Re-Re-Restore Summary: No reset re-entry rebuild re-entry restore re-re-re-restore summary is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Re-Re-Re-Restore Persistence Summary: No reset re-entry rebuild re-entry restore re-re-re-restore persistence summary is recorded yet.
+
Reset Re-entry Rebuild Re-Entry Restore Re-Re-Re-Restore Churn Summary: No reset re-entry rebuild re-entry restore re-re-re-restore churn summary is recorded yet.
+
Reset Re-entry Rebuild Persistence Summary: No reset re-entry rebuild-persistence summary is recorded yet.
+
Reset Re-entry Rebuild Churn Summary: No reset re-entry rebuild-churn summary is recorded yet.
+
Recommendation Quality: No recommendation-quality summary is recorded yet.
+
Confidence Validation: insufficient-data — No confidence-calibration summary is recorded yet.
+
Recent Confidence Outcomes: No recent judged confidence outcomes are recorded yet.
+
Blocked: 1 | Urgent: 1 | Ready: 1 | Deferred: 1
+
  • [Blocked] RepoC: Security drift needs review
    RepoC still needs a governance decision before the security review can settle.
    Why this lane: Governed security drift is still unresolved.
    Next: Open the governed control preview and decide whether to apply CodeQL.
    Last movement: Current run
    Follow-through: Unknown — No follow-through evidence is recorded yet.
    Checkpoint timing: Unknown
    Escalation: Unknown — No stronger follow-through escalation is currently surfaced.
    Recovery / Retirement: None — No follow-through recovery or escalation-retirement signal is currently surfaced.
    Recovery Persistence: None — No follow-through recovery persistence signal is currently surfaced.
    Relapse Churn: None — No relapse churn is currently surfaced.
    Recovery Freshness: None — No follow-through recovery freshness signal is currently surfaced.
    Recovery Memory Reset: None — No follow-through recovery memory reset signal is currently surfaced.
    Recovery Rebuild Strength: None — No follow-through recovery rebuild-strength signal is currently surfaced.
    Recovery Reacquisition: None — No follow-through recovery reacquisition signal is currently surfaced.
    Reacquisition Durability: None — No follow-through reacquisition durability signal is currently surfaced.
    Reacquisition Confidence: None — No follow-through reacquisition confidence-consolidation signal is currently surfaced.
    Reacquisition Softening Decay: None — No reacquisition softening-decay signal is currently surfaced.
    Reacquisition Confidence Retirement: None — No reacquisition confidence-retirement signal is currently surfaced.
    Revalidation Recovery: None — No post-revalidation recovery or confidence re-earning signal is currently surfaced.
    Next checkpoint: Use the next run or linked artifact to confirm whether the recommendation moved.
    Artifact: https://github.com/sample-user/RepoC/issues/1
  • [Needs Attention Now] RepoB: Release checklist reopened
    RepoB needs a small release follow-through pass.
    Why this lane: A previously quiet repo reopened a visible checklist item.
    Next: Re-run the release checklist and close the missing item.
    Last movement: Current run
    Follow-through: Unknown — No follow-through evidence is recorded yet.
    Checkpoint timing: Unknown
    Escalation: Unknown — No stronger follow-through escalation is currently surfaced.
    Recovery / Retirement: None — No follow-through recovery or escalation-retirement signal is currently surfaced.
    Recovery Persistence: None — No follow-through recovery persistence signal is currently surfaced.
    Relapse Churn: None — No relapse churn is currently surfaced.
    Recovery Freshness: None — No follow-through recovery freshness signal is currently surfaced.
    Recovery Memory Reset: None — No follow-through recovery memory reset signal is currently surfaced.
    Recovery Rebuild Strength: None — No follow-through recovery rebuild-strength signal is currently surfaced.
    Recovery Reacquisition: None — No follow-through recovery reacquisition signal is currently surfaced.
    Reacquisition Durability: None — No follow-through reacquisition durability signal is currently surfaced.
    Reacquisition Confidence: None — No follow-through reacquisition confidence-consolidation signal is currently surfaced.
    Reacquisition Softening Decay: None — No reacquisition softening-decay signal is currently surfaced.
    Reacquisition Confidence Retirement: None — No reacquisition confidence-retirement signal is currently surfaced.
    Revalidation Recovery: None — No post-revalidation recovery or confidence re-earning signal is currently surfaced.
    Next checkpoint: Use the next run or linked artifact to confirm whether the recommendation moved.
    Artifact: No linked artifact available yet.
  • [Ready for Manual Action] RepoA: Protect shipped momentum
    A small polish pass would keep the showcase repo strong.
    Why this lane: RepoA is healthy enough that a small polish task would keep it strong.
    Next: Tidy the release notes and publish the next small maintenance release.
    Last movement: Current run
    Follow-through: Unknown — No follow-through evidence is recorded yet.
    Checkpoint timing: Unknown
    Escalation: Unknown — No stronger follow-through escalation is currently surfaced.
    Recovery / Retirement: None — No follow-through recovery or escalation-retirement signal is currently surfaced.
    Recovery Persistence: None — No follow-through recovery persistence signal is currently surfaced.
    Relapse Churn: None — No relapse churn is currently surfaced.
    Recovery Freshness: None — No follow-through recovery freshness signal is currently surfaced.
    Recovery Memory Reset: None — No follow-through recovery memory reset signal is currently surfaced.
    Recovery Rebuild Strength: None — No follow-through recovery rebuild-strength signal is currently surfaced.
    Recovery Reacquisition: None — No follow-through recovery reacquisition signal is currently surfaced.
    Reacquisition Durability: None — No follow-through reacquisition durability signal is currently surfaced.
    Reacquisition Confidence: None — No follow-through reacquisition confidence-consolidation signal is currently surfaced.
    Reacquisition Softening Decay: None — No reacquisition softening-decay signal is currently surfaced.
    Reacquisition Confidence Retirement: None — No reacquisition confidence-retirement signal is currently surfaced.
    Revalidation Recovery: None — No post-revalidation recovery or confidence re-earning signal is currently surfaced.
    Next checkpoint: Use the next run or linked artifact to confirm whether the recommendation moved.
    Artifact: No linked artifact available yet.
  • [Safe to Defer] RepoA: Archive old brainstorm notes
    Backlog cleanup is optional this week.
    Why this lane: This is cleanup-only work with no current pressure.
    Next: Leave this alone unless priorities change.
    Last movement: Current run
    Follow-through: Unknown — No follow-through evidence is recorded yet.
    Checkpoint timing: Unknown
    Escalation: Unknown — No stronger follow-through escalation is currently surfaced.
    Recovery / Retirement: None — No follow-through recovery or escalation-retirement signal is currently surfaced.
    Recovery Persistence: None — No follow-through recovery persistence signal is currently surfaced.
    Relapse Churn: None — No relapse churn is currently surfaced.
    Recovery Freshness: None — No follow-through recovery freshness signal is currently surfaced.
    Recovery Memory Reset: None — No follow-through recovery memory reset signal is currently surfaced.
    Recovery Rebuild Strength: None — No follow-through recovery rebuild-strength signal is currently surfaced.
    Recovery Reacquisition: None — No follow-through recovery reacquisition signal is currently surfaced.
    Reacquisition Durability: None — No follow-through reacquisition durability signal is currently surfaced.
    Reacquisition Confidence: None — No follow-through reacquisition confidence-consolidation signal is currently surfaced.
    Reacquisition Softening Decay: None — No reacquisition softening-decay signal is currently surfaced.
    Reacquisition Confidence Retirement: None — No reacquisition confidence-retirement signal is currently surfaced.
    Revalidation Recovery: None — No post-revalidation recovery or confidence re-earning signal is currently surfaced.
    Next checkpoint: Use the next run or linked artifact to confirm whether the recommendation moved.
    Artifact: No linked artifact available yet.
+
Recently Changed:
+
  • No recent operator changes were loaded.
+
+
+ +
+

Weekly Review Pack

+
+
+
Product Mode: Action Sync: use this artifact to confirm the local campaign story first, then preview or apply GitHub, GitHub Projects, or Notion mirrors only after the repo-level decision is settled.
+
Artifact Role: Local report remains authoritative; campaigns, writeback, and GitHub Projects are managed mirrors of the decision already made here.
+
Suggested Reading Order: Read Review Queue first, then the control-center headline and primary target, then the campaign preview or writeback section before syncing outward.
+
Next Best Workflow Step: Keep the local artifact as the source of truth, then use campaign preview or writeback only when the repo decision is already clear.
+
Portfolio Headline: RepoC still needs security follow-through before the queue will calm down.
+
Run Changes: Average score moved +0.020; 0 meaningful improvements, 0 regressions, 1 promotions, and 1 demotions were recorded.
+
Queue Pressure: 1 blocked, 1 urgent, 1 ready, and 1 deferred item(s) are currently in the queue.
+
Trust / Actionability: verify-first — The recommendation is sound, but recent reopen noise means it should still be reviewed before acting.
+
What To Do This Week: Review RepoC first, then close RepoB's reopened checklist item. Start with RepoC if you need a concrete place to begin.
+
Portfolio Catalog: No portfolio catalog contract is recorded yet.
+
Operating Paths: No normalized operating-path contract is recorded yet.
+
Intent Alignment: Intent alignment cannot be judged until a portfolio catalog contract exists.
+
Scorecards: No maturity scorecard is recorded yet.
+
Implementation Hotspots: No meaningful implementation hotspots are currently surfaced.
+
Operator Outcomes: Not enough operator history is recorded yet to judge whether recent actions are improving portfolio outcomes.
+
Operator Effectiveness: Not enough judged recommendation history is recorded yet to judge operator effectiveness.
+
High-Pressure Queue Trend: High-pressure queue trend is not ready yet.
+
Action Sync Readiness: No current campaign needs Action Sync yet, so the safest next move is to keep the story local.
+
Next Action Sync Step: Stay local for now; no current campaign needs preview or apply.
+
Apply Packet: No current campaign has a safe execution handoff yet, so the local story should stay local for now.
+
Next Apply Candidate: Stay local for now; no current campaign has a safe execution handoff.
+
Action Sync Command Hint: No Action Sync command is recommended yet.
+
Post-Apply Monitoring: No recent Action Sync apply needs post-apply monitoring yet, so the local weekly story can stay local.
+
Next Monitoring Step: Stay local for now; no recent Action Sync apply needs post-apply follow-up yet.
+
Campaign Tuning: Campaign tuning stays neutral until there is enough outcome history to bias tied recommendations.
+
Next Tie-Break Candidate: No current campaign needs a tie-break candidate yet.
+
Historical Portfolio Intelligence: Historical portfolio intelligence is still thin, so the weekly story should stay grounded in the current run and recent operator queue.
+
Next Historical Focus: Stay local for now; no repo has enough cross-run intervention evidence to demand a historical follow-up read yet.
+
Automation Guidance: Automation guidance stays quiet until a campaign has a clearly safe preview, follow-up, or manual-only posture.
+
Next Safe Automation Step: Stay local for now; no current campaign has a stronger safe automation posture than manual review.
+
Approval Workflow: No current approval needs review yet, so the approval workflow can stay local for now.
+
Next Approval Review: Stay local for now; no current approval needs review.
+ +
+

Action Sync Readiness

+
Summary: No current campaign needs Action Sync yet, so the safest next move is to keep the story local.
+
Next Step: Stay local for now; no current campaign needs preview or apply.
+
State: idle
+
  • No supporting evidence is currently surfaced.
+
+ +
+

Apply Packet

+
Summary: No current campaign has a safe execution handoff yet, so the local story should stay local for now.
+
Next Candidate: Stay local for now; no current campaign has a safe execution handoff.
+
State: idle
+
  • No supporting evidence is currently surfaced.
+
+ +
+

Post-Apply Monitoring

+
Summary: No recent Action Sync apply needs post-apply monitoring yet, so the local weekly story can stay local.
+
Next Step: Stay local for now; no recent Action Sync apply needs post-apply follow-up yet.
+
State: idle
+
  • No supporting evidence is currently surfaced.
+
+ +
+

Campaign Tuning

+
Summary: Campaign tuning stays neutral until there is enough outcome history to bias tied recommendations.
+
Next Tie-Break Candidate: No current campaign needs a tie-break candidate yet.
+
State: idle
+
  • No supporting evidence is currently surfaced.
+
+ +
+

Historical Portfolio Intelligence

+
Summary: Historical portfolio intelligence is still thin, so the weekly story should stay grounded in the current run and recent operator queue.
+
Next Focus: Stay local for now; no repo has enough cross-run intervention evidence to demand a historical follow-up read yet.
+
State: idle
+
  • No supporting evidence is currently surfaced.
+
+ +
+

Automation Guidance

+
Summary: Automation guidance stays quiet until a campaign has a clearly safe preview, follow-up, or manual-only posture.
+
Next Step: Stay local for now; no current campaign has a stronger safe automation posture than manual review.
+
State: idle
+
  • No supporting evidence is currently surfaced.
+
+ +
+

Approval Workflow

+
Summary: No current approval needs review yet, so the approval workflow can stay local for now.
+
Next Approval Review: Stay local for now; no current approval needs review.
+
State: idle
+
  • No supporting evidence is currently surfaced.
+
+ +
+

Operator Focus

+
Summary: 2 item(s) need immediate action first, led by RepoC, RepoB.
+
Next Checkpoint: Use the next run or linked artifact to confirm whether the recommendation moved.
+
State: act-now
+
  • RepoC — Governed security drift is still unresolved.
  • RepoB — A previously quiet repo reopened a visible checklist item.
  • RepoA — No follow-through evidence is recorded yet.
  • RepoA — No follow-through evidence is recorded yet.
+
+ +
Operator Focus: 2 item(s) need immediate action first, led by RepoC, RepoB.
+
Next Checkpoint: Use the next run or linked artifact to confirm whether the recommendation moved.
+
Act Now:
  • RepoC: Security drift needs review — Governed security drift is still unresolved.
  • RepoB: Release checklist reopened — A previously quiet repo reopened a visible checklist item.
Watch Closely:
  • RepoA: Protect shipped momentum — No follow-through evidence is recorded yet.
  • RepoA: Archive old brainstorm notes — No follow-through evidence is recorded yet.
Improving:
  • No clearly improving hotspots are currently surfaced.
Fragile:
  • No fragile hotspots are currently surfaced.
Revalidate:
  • No revalidation hotspots are currently surfaced.
+

Top Attention

+
  • RepoC: Security drift needs review
    What Changed: Current run
    Why It Matters: Governed security drift is still unresolved.
    What To Do Next: Open the governed control preview and decide whether to apply CodeQL.
    Operator Focus: Act Now: Governed security drift is still unresolved.
    Catalog: No portfolio catalog contract is recorded yet.
    Operating Path: Unspecified (legacy confidence) — No operating-path rationale is recorded yet.
    Intent Alignment: missing-contract: Intent alignment cannot be judged until a portfolio catalog contract exists.
    Scorecard: No maturity scorecard is recorded yet.
    Maturity Gap: No maturity gap summary is recorded yet.
    Evidence: Operator Focus: Act Now: Governed security drift is still unresolved.; Action Sync: Action Sync: stay local until a campaign has meaningful actions and healthy writeback prerequisites.; Automation Guidance: Automation Guidance: keep the next step human-led until a bounded safe posture is surfaced.; Approval Workflow: Approval Workflow: no current approval needs review yet.
    Checkpoint Timing: Unknown
    Next Checkpoint: Use the next run or linked artifact to confirm whether the recommendation moved.
  • RepoB: Release checklist reopened
    What Changed: Current run
    Why It Matters: A previously quiet repo reopened a visible checklist item.
    What To Do Next: Re-run the release checklist and close the missing item.
    Operator Focus: Act Now: A previously quiet repo reopened a visible checklist item.
    Catalog: No portfolio catalog contract is recorded yet.
    Operating Path: Unspecified (legacy confidence) — No operating-path rationale is recorded yet.
    Intent Alignment: missing-contract: Intent alignment cannot be judged until a portfolio catalog contract exists.
    Scorecard: No maturity scorecard is recorded yet.
    Maturity Gap: No maturity gap summary is recorded yet.
    Evidence: Operator Focus: Act Now: A previously quiet repo reopened a visible checklist item.; Action Sync: Action Sync: stay local until a campaign has meaningful actions and healthy writeback prerequisites.; Automation Guidance: Automation Guidance: keep the next step human-led until a bounded safe posture is surfaced.; Approval Workflow: Approval Workflow: no current approval needs review yet.
    Checkpoint Timing: Unknown
    Next Checkpoint: Use the next run or linked artifact to confirm whether the recommendation moved.
  • RepoA: Protect shipped momentum
    What Changed: Current run
    Why It Matters: RepoA is healthy enough that a small polish task would keep it strong.
    What To Do Next: Tidy the release notes and publish the next small maintenance release.
    Operator Focus: Watch Closely: No follow-through evidence is recorded yet.
    Catalog: No portfolio catalog contract is recorded yet.
    Operating Path: Unspecified (legacy confidence) — No operating-path rationale is recorded yet.
    Intent Alignment: missing-contract: Intent alignment cannot be judged until a portfolio catalog contract exists.
    Scorecard: No maturity scorecard is recorded yet.
    Maturity Gap: No maturity gap summary is recorded yet.
    Evidence: Operator Focus: Watch Closely: No follow-through evidence is recorded yet.; Action Sync: Action Sync: stay local until a campaign has meaningful actions and healthy writeback prerequisites.; Automation Guidance: Automation Guidance: keep the next step human-led until a bounded safe posture is surfaced.; Approval Workflow: Approval Workflow: no current approval needs review yet.
    Checkpoint Timing: Unknown
    Next Checkpoint: Use the next run or linked artifact to confirm whether the recommendation moved.
  • RepoA: Archive old brainstorm notes
    What Changed: Current run
    Why It Matters: This is cleanup-only work with no current pressure.
    What To Do Next: Leave this alone unless priorities change.
    Operator Focus: Watch Closely: No follow-through evidence is recorded yet.
    Catalog: No portfolio catalog contract is recorded yet.
    Operating Path: Unspecified (legacy confidence) — No operating-path rationale is recorded yet.
    Intent Alignment: missing-contract: Intent alignment cannot be judged until a portfolio catalog contract exists.
    Scorecard: No maturity scorecard is recorded yet.
    Maturity Gap: No maturity gap summary is recorded yet.
    Evidence: Operator Focus: Watch Closely: No follow-through evidence is recorded yet.; Action Sync: Action Sync: stay local until a campaign has meaningful actions and healthy writeback prerequisites.; Automation Guidance: Automation Guidance: keep the next step human-led until a bounded safe posture is surfaced.; Approval Workflow: Approval Workflow: no current approval needs review yet.
    Checkpoint Timing: Unknown
    Next Checkpoint: Use the next run or linked artifact to confirm whether the recommendation moved.
+
+
+

Top Repo Drilldowns

+ +
+

RepoC — 0.44 (wip)

+
Current State: Score 0.44, grade D, wip tier, Python, trend: No history is recorded yet, so this view is using the current run only.
+
What Changed: Regressed 0.080 versus the last run. Tier moved functional -> wip. Score regressed 0.080 since the last run. Tier moved functional -> wip.
+
Why It Matters: Strongest drivers: Security (0.28), Testing (0.18). Biggest drags: Testing (0.18), Security (0.28). Next tier gap: Needs +0.31 to reach shipped.
+
Where To Start: No meaningful implementation hotspot is currently surfaced.
+
What To Do Next: Open the governed control preview and decide whether to apply CodeQL. No action rationale is recorded yet.
+
Operator Focus: Act Now: Governed security drift is still unresolved.
+
Catalog: No portfolio catalog contract is recorded yet.
+
Operating Path: Unspecified (legacy confidence) — No operating-path rationale is recorded yet.
+
Intent Alignment: missing-contract: Intent alignment cannot be judged until a portfolio catalog contract exists.
+
Scorecard: No maturity scorecard is recorded yet.
+
Maturity Gap: No maturity gap summary is recorded yet.
+
Evidence: Where To Start: No meaningful implementation hotspot is currently surfaced.; Operator Focus: Act Now: Governed security drift is still unresolved.; Action Sync: Action Sync: stay local until a campaign has meaningful actions and healthy writeback prerequisites.; Automation Guidance: Automation Guidance: keep the next step human-led until a bounded safe posture is surfaced.
+
Checkpoint Timing: Unknown
+
What Would Count As Progress: Use the next run or linked artifact to confirm whether the recommendation moved.
+
+ +
+

RepoB — 0.62 (functional)

+
Current State: Score 0.62, grade C, functional tier, TypeScript, trend: No history is recorded yet, so this view is using the current run only.
+
What Changed: Regressed 0.060 versus the last run. Score regressed 0.060 since the last run.
+
Why It Matters: Strongest drivers: Security (0.66), Testing (0.55), Release (0.48). Biggest drags: Release (0.48), Testing (0.55), Security (0.66). Next tier gap: Needs +0.13 to reach shipped.
+
Where To Start: No meaningful implementation hotspot is currently surfaced.
+
What To Do Next: Re-run the release checklist and close the missing item. No action rationale is recorded yet.
+
Operator Focus: Act Now: A previously quiet repo reopened a visible checklist item.
+
Catalog: No portfolio catalog contract is recorded yet.
+
Operating Path: Unspecified (legacy confidence) — No operating-path rationale is recorded yet.
+
Intent Alignment: missing-contract: Intent alignment cannot be judged until a portfolio catalog contract exists.
+
Scorecard: No maturity scorecard is recorded yet.
+
Maturity Gap: No maturity gap summary is recorded yet.
+
Evidence: Where To Start: No meaningful implementation hotspot is currently surfaced.; Operator Focus: Act Now: A previously quiet repo reopened a visible checklist item.; Action Sync: Action Sync: stay local until a campaign has meaningful actions and healthy writeback prerequisites.; Automation Guidance: Automation Guidance: keep the next step human-led until a bounded safe posture is surfaced.
+
Checkpoint Timing: Unknown
+
What Would Count As Progress: Use the next run or linked artifact to confirm whether the recommendation moved.
+
+ +
+

RepoA — 0.88 (shipped)

+
Current State: Score 0.88, grade A, shipped tier, Python, trend: No history is recorded yet, so this view is using the current run only.
+
What Changed: Improved 0.090 versus the last run. Tier moved functional -> shipped. Score improved 0.090 since the last run. Tier moved functional -> shipped.
+
Why It Matters: Strongest drivers: Testing (0.90), Documentation (0.85), Security (0.82). Biggest drags: Security (0.82), Documentation (0.85), Testing (0.90). Next tier gap: Already at shipped tier; focus on protecting momentum and preventing drift.
+
Where To Start: No meaningful implementation hotspot is currently surfaced.
+
What To Do Next: Tidy the release notes and publish the next small maintenance release. No action rationale is recorded yet.
+
Operator Focus: Watch Closely: No follow-through evidence is recorded yet.
+
Catalog: No portfolio catalog contract is recorded yet.
+
Operating Path: Unspecified (legacy confidence) — No operating-path rationale is recorded yet.
+
Intent Alignment: missing-contract: Intent alignment cannot be judged until a portfolio catalog contract exists.
+
Scorecard: No maturity scorecard is recorded yet.
+
Maturity Gap: No maturity gap summary is recorded yet.
+
Evidence: Where To Start: No meaningful implementation hotspot is currently surfaced.; Operator Focus: Watch Closely: No follow-through evidence is recorded yet.; Action Sync: Action Sync: stay local until a campaign has meaningful actions and healthy writeback prerequisites.; Automation Guidance: Automation Guidance: keep the next step human-led until a bounded safe posture is surfaced.
+
Checkpoint Timing: Unknown
+
What Would Count As Progress: Use the next run or linked artifact to confirm whether the recommendation moved.
+
+ +
+
+
+ +
+

Top Attention / Next Action

+
+
  • RepoC: Security drift needs review
    Why it matters: Governed security drift is still unresolved.
    What to do next: Open the governed control preview and decide whether to apply CodeQL.
    Operator Focus: Act Now: Governed security drift is still unresolved.
    Catalog: No portfolio catalog contract is recorded yet.
    Operating Path: Unspecified (legacy confidence) — No operating-path rationale is recorded yet.
    Intent Alignment: missing-contract: Intent alignment cannot be judged until a portfolio catalog contract exists.
    Scorecard: No maturity scorecard is recorded yet.
    Maturity Gap: No maturity gap summary is recorded yet.
    Action Sync: Action Sync: stay local until a campaign has meaningful actions and healthy writeback prerequisites.
    Apply Packet: Apply Packet: no current execution handoff is surfaced.
    Post-Apply Monitoring: Post-Apply Monitoring: no recent Action Sync apply needs follow-up yet.
    Campaign Tuning: Campaign Tuning: recommendations stay neutral until more outcome history is available.
    Approval Workflow: Approval Workflow: no current approval needs review yet.
    Checkpoint timing: Unknown
  • RepoB: Release checklist reopened
    Why it matters: A previously quiet repo reopened a visible checklist item.
    What to do next: Re-run the release checklist and close the missing item.
    Operator Focus: Act Now: A previously quiet repo reopened a visible checklist item.
    Catalog: No portfolio catalog contract is recorded yet.
    Operating Path: Unspecified (legacy confidence) — No operating-path rationale is recorded yet.
    Intent Alignment: missing-contract: Intent alignment cannot be judged until a portfolio catalog contract exists.
    Scorecard: No maturity scorecard is recorded yet.
    Maturity Gap: No maturity gap summary is recorded yet.
    Action Sync: Action Sync: stay local until a campaign has meaningful actions and healthy writeback prerequisites.
    Apply Packet: Apply Packet: no current execution handoff is surfaced.
    Post-Apply Monitoring: Post-Apply Monitoring: no recent Action Sync apply needs follow-up yet.
    Campaign Tuning: Campaign Tuning: recommendations stay neutral until more outcome history is available.
    Approval Workflow: Approval Workflow: no current approval needs review yet.
    Checkpoint timing: Unknown
  • RepoA: Protect shipped momentum
    Why it matters: RepoA is healthy enough that a small polish task would keep it strong.
    What to do next: Tidy the release notes and publish the next small maintenance release.
    Operator Focus: Watch Closely: No follow-through evidence is recorded yet.
    Catalog: No portfolio catalog contract is recorded yet.
    Operating Path: Unspecified (legacy confidence) — No operating-path rationale is recorded yet.
    Intent Alignment: missing-contract: Intent alignment cannot be judged until a portfolio catalog contract exists.
    Scorecard: No maturity scorecard is recorded yet.
    Maturity Gap: No maturity gap summary is recorded yet.
    Action Sync: Action Sync: stay local until a campaign has meaningful actions and healthy writeback prerequisites.
    Apply Packet: Apply Packet: no current execution handoff is surfaced.
    Post-Apply Monitoring: Post-Apply Monitoring: no recent Action Sync apply needs follow-up yet.
    Campaign Tuning: Campaign Tuning: recommendations stay neutral until more outcome history is available.
    Approval Workflow: Approval Workflow: no current approval needs review yet.
    Checkpoint timing: Unknown
+
+
+ +
+

Analyst View

+
+
+
Profile: default
+
Collection: all
+
showcase: 1
+
+
+

Profile Leaders

+ + + +
RepoProfile ScoreTier
RepoA0.880shipped
RepoB0.620functional
RepoC0.440wip
+
+
+
+ +
+

Decision Lenses

+
ship readiness
0.71
Delivery readiness
showcase value
0.63
Story and polish
+
+ +
+

Security Overview

+
+
+
Avg Security Score
+
0.58
+
Portfolio-wide merged security posture.
+
+
+
GitHub Coverage
+
3
+
Repos with GitHub-native security evidence available.
+
+
+
Scorecard Coverage
+
0
+
Repos with external Scorecard evidence loaded.
+
+
+
Open Alerts
+
3
+
Combined code and secret scanning alerts.
+
+
+
+

Completeness vs Interest

+ +
+
+ +
+

All Repos

+
+ + + + + +
+ + + + + + +
RepoProfileGradeScoreInterestTierRiskLanguageCollectionsTrendDescription
RepoA0.880A0.8800.610shippedbaselinePythonshowcaseA polished shipped project.
Next: Publish the next maintenance release
Gap: Already at shipped tier; focus on protecting momentum and preventing drift.
RepoB0.620C0.6200.370functionalelevatedTypeScriptA functional project with follow-through left to do.
Next: Close the reopened release item
Gap: Needs +0.13 to reach shipped.
RepoC0.440D0.4400.160wipelevatedPythonA risky work-in-progress project.
Next: Preview governance controls
Gap: Needs +0.31 to reach shipped.
+
+ +
+

Repo Drilldowns

+
+ +
+

RepoA — 0.88 (shipped)

+
Current State: Score 0.88, grade A, shipped tier, Python, trend: No history is recorded yet, so this view is using the current run only.
+
Description: A polished shipped project.
+
Why This Repo Looks This Way: Strongest drivers: Testing (0.90), Documentation (0.85), Security (0.82). Biggest drags: Security (0.82), Documentation (0.85), Testing (0.90). Next tier gap: Already at shipped tier; focus on protecting momentum and preventing drift.
+
What Changed: Improved 0.090 versus the last run. Tier moved functional -> shipped. Score improved 0.090 since the last run. Tier moved functional -> shipped.
+
Hotspot Context: Keep release notes fresh
+
Where To Start: No meaningful implementation hotspot is currently surfaced.
+
  • No implementation hotspots are currently surfaced.
+
What To Do Next: Tidy the release notes and publish the next small maintenance release.
+
Rationale: No action rationale is recorded yet.
+
Follow-Through: Unknown: No follow-through evidence is recorded yet.
+
Checkpoint Timing: Unknown
+
Escalation: Unknown: No stronger follow-through escalation is currently surfaced.
+
What Would Count As Progress: Use the next run or linked artifact to confirm whether the recommendation moved.
+
Linked Artifact: No linked artifact available yet.
+
+ +
+

RepoB — 0.62 (functional)

+
Current State: Score 0.62, grade C, functional tier, TypeScript, trend: No history is recorded yet, so this view is using the current run only.
+
Description: A functional project with follow-through left to do.
+
Why This Repo Looks This Way: Strongest drivers: Security (0.66), Testing (0.55), Release (0.48). Biggest drags: Release (0.48), Testing (0.55), Security (0.66). Next tier gap: Needs +0.13 to reach shipped.
+
What Changed: Regressed 0.060 versus the last run. Score regressed 0.060 since the last run.
+
Hotspot Context: Finish release checklist
+
Where To Start: No meaningful implementation hotspot is currently surfaced.
+
  • No implementation hotspots are currently surfaced.
+
What To Do Next: Re-run the release checklist and close the missing item.
+
Rationale: No action rationale is recorded yet.
+
Follow-Through: Unknown: No follow-through evidence is recorded yet.
+
Checkpoint Timing: Unknown
+
Escalation: Unknown: No stronger follow-through escalation is currently surfaced.
+
What Would Count As Progress: Use the next run or linked artifact to confirm whether the recommendation moved.
+
Linked Artifact: No linked artifact available yet.
+
+ +
+

RepoC — 0.44 (wip)

+
Current State: Score 0.44, grade D, wip tier, Python, trend: No history is recorded yet, so this view is using the current run only.
+
Description: A risky work-in-progress project.
+
Why This Repo Looks This Way: Strongest drivers: Security (0.28), Testing (0.18). Biggest drags: Testing (0.18), Security (0.28). Next tier gap: Needs +0.31 to reach shipped.
+
What Changed: Regressed 0.080 versus the last run. Tier moved functional -> wip. Score regressed 0.080 since the last run. Tier moved functional -> wip.
+
Hotspot Context: Security posture needs attention
+
Where To Start: No meaningful implementation hotspot is currently surfaced.
+
  • No implementation hotspots are currently surfaced.
+
What To Do Next: Open the governed control preview and decide whether to apply CodeQL.
+
Rationale: No action rationale is recorded yet.
+
Follow-Through: Unknown: No follow-through evidence is recorded yet.
+
Checkpoint Timing: Unknown
+
Escalation: Unknown: No stronger follow-through escalation is currently surfaced.
+
What Would Count As Progress: Use the next run or linked artifact to confirm whether the recommendation moved.
+
Linked Artifact: No linked artifact available yet.
+
+ +
+
+ +
+

Portfolio Trends

+

Not enough historical data for trends. Run audits over time to see portfolio evolution.

+
+ +
+

Run Changes

+
+
+
Summary: Average score moved +0.020; 0 meaningful improvements, 0 regressions, 1 promotions, and 1 demotions were recorded.
+
Why It Matters: 1 blocked, 1 urgent, 1 ready, and 1 deferred item(s) are currently in the queue.
+
What To Do Next: Review RepoC first, then close RepoB's reopened checklist item. Start with RepoC if you need a concrete place to begin.
+
Improving: 0 | Regressing: 0
+
Promotions: 1 | Demotions: 1
+
Security / Governance: 1 / 1
+
+
+ + + +
RepoDeltaTier Change
RepoA+0.090functional → shipped
RepoC-0.080functional → wip
+
+
+
+ +
+

Compare

+
+
+
Average score delta: +0.020
+ + + +
LensDelta
ship_readiness+0.030
security_posture-0.010
+
+
+

Top Movers

+ + + +
RepoScore DeltaTier
RepoA+0.090functional → shipped
RepoC-0.080functional → wip
+
+
+
+ +
+

Scenario Preview

+
+
+
Projected average score delta: +0.000
+
Projected promotions: 0
+
Selected repos: 3
+
+
+ + + +
LeverLensReposAvg Lift
Publish the next maintenance release1+0.000
Close the reopened release item1+0.000
Preview governance controls1+0.000
+
+
+
+ +
+

Governance Operator State

+
Headline: Governed security drift needs review.
Status: drifted
Approved: no | Needs Re-Approval: no
Drift Count: 1 | Applied Results: 0 | Rollback Available: 1
+ + + +
RepoStateActionExpected LiftSource
RepoCreadyEnable CodeQL default setup0.12github
+
+ +
+

Campaign

+
+
+
Campaign: Security Review
+
Actions: 1
+
Repos: 1
+
Sync mode: reconcile
+
Drift: 1
+
Action Sync: Stay local for now; no current campaign needs preview or apply.
+
Apply Packet: No current campaign has a safe execution handoff yet, so the local story should stay local for now.
+
Command Hint: No Action Sync command is recommended yet.
+
Post-Apply Monitoring: No recent Action Sync apply needs post-apply monitoring yet, so the local weekly story can stay local.
+
Next Monitoring Step: Stay local for now; no recent Action Sync apply needs post-apply follow-up yet.
+
Campaign Tuning: Campaign tuning stays neutral until there is enough outcome history to bias tied recommendations.
+
Next Tie-Break Candidate: No current campaign needs a tie-break candidate yet.
+
Automation Guidance: Automation guidance stays quiet until a campaign has a clearly safe preview, follow-up, or manual-only posture.
+
Next Safe Automation Step: Stay local for now; no current campaign has a stronger safe automation posture than manual review.
+
Approval Workflow: No current approval needs review yet, so the approval workflow can stay local for now.
+
Next Approval Review: Stay local for now; no current approval needs review.
+
GitHub Projects: disabled + (— #0, 0 items)
+
+
+ + + +
RepoManaged IssueTopicsProjectsNotion
RepoC[Repo Auditor] Security Review101
+
+
+
+ +
+

Writeback Results

+
Mode: apply | + Target: github
+
Managed drift: 1
+ + + +
RepoTargetStatusDetails
sample-user/RepoCgithub-issuecreatedhttps://github.com/sample-user/RepoC/issues/1
+
+ +
+

Tech Radar

+ + + +
LanguageReposTrendCategory
TypeScript44▁▂▆▆▆▆▆▇▇█Adopt
Rust32▁▁▁▁▁▁▁███Hold
Python19▁▂▄▄▄▄▄▅██Adopt
Swift16▅▅▅▅▅▅▅▅▅▅Hold
Shell4▁▁▁▁▂▂▂▂██Trial
Makefile3▁█▆▆▆▆▆▆▆▆Trial
GDScript3▅▅▅▅▅▅▅▅▅▅Hold
JavaScript2▅▅▅▅▅▅▅▅▅▅Hold
PLpgSQL1▅▅▅▅▅▅▅▅▅▅Hold
Go1▁▁████████Trial
+
+ +
+

Tier Distribution

+
Shipped
1
Functional
1
Wip
1
Skeleton
0
Abandoned
0
+
+ + + + + + \ No newline at end of file diff --git a/output/demo/demo-report.json b/output/demo/demo-report.json new file mode 100644 index 0000000..5d48864 --- /dev/null +++ b/output/demo/demo-report.json @@ -0,0 +1,557 @@ +{ + "username": "sample-user", + "generated_at": "2026-04-12T12:00:00+00:00", + "repos_audited": 3, + "total_repos": 3, + "average_score": 0.65, + "portfolio_grade": "B", + "portfolio_health_score": 0.69, + "tier_distribution": { + "shipped": 1, + "functional": 1, + "wip": 1, + "skeleton": 0, + "abandoned": 0 + }, + "language_distribution": { + "Python": 2, + "TypeScript": 1 + }, + "profiles": { + "default": { + "description": "Balanced portfolio scoring." + } + }, + "collections": { + "showcase": { + "description": "Best examples to show other people.", + "repos": [ + { + "name": "RepoA", + "reason": "Strong showcase candidate." + } + ] + } + }, + "lenses": { + "ship_readiness": { + "description": "Delivery readiness", + "average_score": 0.71 + }, + "showcase_value": { + "description": "Story and polish", + "average_score": 0.63 + } + }, + "scenario_summary": { + "top_levers": [ + { + "key": "testing", + "title": "Strengthen tests", + "lens": "ship_readiness", + "repo_count": 2, + "average_expected_lens_delta": 0.09, + "projected_tier_promotions": 1 + } + ], + "portfolio_projection": { + "selected_repo_count": 3, + "projected_average_score_delta": 0.05, + "projected_tier_promotions": 1 + } + }, + "security_posture": { + "average_score": 0.58, + "critical_repos": [ + "RepoC" + ], + "repos_with_secrets": [], + "provider_coverage": { + "github": { + "available_repos": 3, + "total_repos": 3 + } + }, + "open_alerts": { + "code_scanning": 2, + "secret_scanning": 1 + } + }, + "security_governance_preview": [ + { + "repo": "RepoC", + "priority": "high", + "title": "Enable CodeQL default setup", + "expected_posture_lift": 0.12, + "effort": "medium", + "source": "github", + "why": "Code scanning is not configured." + } + ], + "governance_summary": { + "headline": "Governed security drift needs review.", + "status": "drifted", + "needs_reapproval": false, + "drift_count": 1, + "applyable_count": 1, + "applied_count": 0, + "rollback_available_count": 1, + "top_actions": [ + { + "repo": "RepoC", + "title": "Enable CodeQL default setup", + "operator_state": "ready", + "expected_posture_lift": 0.12, + "source": "github" + } + ] + }, + "campaign_summary": { + "campaign_type": "security-review", + "label": "Security Review", + "action_count": 1, + "repo_count": 1 + }, + "writeback_preview": { + "sync_mode": "reconcile", + "repos": [ + { + "repo": "RepoC", + "topics": [ + "ghra-call-security-review" + ], + "issue_title": "[Repo Auditor] Security Review", + "notion_action_count": 1 + } + ] + }, + "writeback_results": { + "mode": "apply", + "target": "github", + "results": [ + { + "repo_full_name": "sample-user/RepoC", + "target": "github-issue", + "status": "created", + "url": "https://github.com/sample-user/RepoC/issues/1" + } + ] + }, + "managed_state_drift": [ + { + "repo_full_name": "sample-user/RepoC", + "target": "github-issue", + "drift_state": "managed-issue-edited" + } + ], + "governance_drift": [ + { + "repo_full_name": "sample-user/RepoC", + "drift_type": "already-enabled" + } + ], + "review_summary": { + "review_id": "sample-review-1", + "status": "open", + "source_run_id": "sample-user:2026-04-12T12:00:00+00:00" + }, + "review_targets": [ + { + "repo": "RepoC", + "title": "Security posture needs attention", + "severity": 0.84, + "next_step": "Preview governance controls", + "decision_hint": "ready-for-governance-approval", + "safe_to_defer": false + } + ], + "review_history": [ + { + "review_id": "sample-review-1", + "generated_at": "2026-04-12T12:00:00+00:00", + "material_change_count": 2, + "status": "open", + "decision_state": "needs-review", + "sync_state": "local-only", + "emitted": true + } + ], + "material_changes": [ + { + "change_type": "security-change", + "repo": "RepoC", + "severity": 0.84, + "title": "Security posture needs attention" + }, + { + "change_type": "reopened-review", + "repo": "RepoB", + "severity": 0.51, + "title": "Release checklist reopened" + } + ], + "operator_summary": { + "headline": "RepoC still needs security follow-through before the queue will calm down.", + "counts": { + "blocked": 1, + "urgent": 1, + "ready": 1, + "deferred": 1 + }, + "source_run_id": "sample-user:2026-04-12T12:00:00+00:00", + "next_recommended_run_mode": "incremental", + "watch_strategy": "adaptive", + "watch_decision_summary": "The current baseline is still compatible, so incremental watch remains safe for the next run.", + "what_changed": "RepoC drift needs review and RepoB reopened a release checklist item.", + "why_it_matters": "Live drift is still present, so security work should stay ahead of lower-pressure cleanup.", + "what_to_do_next": "Review RepoC first, then close RepoB's reopened checklist item.", + "trend_summary": "Queue pressure is stable but still sticky.", + "follow_through_summary": "One urgent item is still repeating in the recent window.", + "primary_target_reason": "RepoC remains the top target because live drift is still open.", + "closure_guidance": "Clear RepoC's security drift before moving to lower-pressure work.", + "primary_target_trust_policy": "verify-first", + "primary_target_trust_policy_reason": "The recommendation is sound, but recent reopen noise means it should still be reviewed before acting.", + "control_center_reference": "output/demo/operator-control-center-sample-user-2026-04-12.json" + }, + "operator_queue": [ + { + "repo": "RepoC", + "title": "Security drift needs review", + "lane": "blocked", + "lane_label": "Blocked", + "lane_reason": "Governed security drift is still unresolved.", + "kind": "campaign", + "priority": 1, + "summary": "RepoC still needs a governance decision before the security review can settle.", + "recommended_action": "Open the governed control preview and decide whether to apply CodeQL.", + "repo_url": "https://github.com/sample-user/RepoC", + "links": [ + { + "label": "GitHub Issue", + "url": "https://github.com/sample-user/RepoC/issues/1" + } + ] + }, + { + "repo": "RepoB", + "title": "Release checklist reopened", + "lane": "urgent", + "lane_label": "Needs Attention Now", + "lane_reason": "A previously quiet repo reopened a visible checklist item.", + "kind": "review", + "priority": 2, + "summary": "RepoB needs a small release follow-through pass.", + "recommended_action": "Re-run the release checklist and close the missing item.", + "repo_url": "https://github.com/sample-user/RepoB" + }, + { + "repo": "RepoA", + "title": "Protect shipped momentum", + "lane": "ready", + "lane_label": "Ready for Manual Action", + "lane_reason": "RepoA is healthy enough that a small polish task would keep it strong.", + "kind": "maintenance", + "priority": 3, + "summary": "A small polish pass would keep the showcase repo strong.", + "recommended_action": "Tidy the release notes and publish the next small maintenance release.", + "repo_url": "https://github.com/sample-user/RepoA" + }, + { + "repo": "RepoA", + "title": "Archive old brainstorm notes", + "lane": "deferred", + "lane_label": "Safe to Defer", + "lane_reason": "This is cleanup-only work with no current pressure.", + "kind": "cleanup", + "priority": 4, + "summary": "Backlog cleanup is optional this week.", + "recommended_action": "Leave this alone unless priorities change.", + "repo_url": "https://github.com/sample-user/RepoA" + } + ], + "run_change_summary": "Average score moved +0.020; 0 meaningful improvements, 0 regressions, 1 promotions, and 1 demotions were recorded.", + "run_change_counts": { + "new_repos": 0, + "removed_repos": 0, + "tier_promotions": 1, + "tier_demotions": 1, + "score_improvements": 0, + "score_regressions": 0, + "security_changes": 1, + "hotspot_changes": 2, + "collection_changes": 1, + "notable_repo_changes": 2 + }, + "audits": [ + { + "metadata": { + "name": "RepoA", + "full_name": "sample-user/RepoA", + "html_url": "https://github.com/sample-user/RepoA", + "description": "A polished shipped project.", + "language": "Python", + "private": false, + "archived": false, + "stars": 12, + "topics": [ + "python", + "cli" + ] + }, + "overall_score": 0.88, + "interest_score": 0.61, + "grade": "A", + "completeness_tier": "shipped", + "badges": [ + "fresh", + "tested" + ], + "flags": [], + "lenses": { + "ship_readiness": { + "score": 0.89, + "summary": "Ready" + }, + "momentum": { + "score": 0.75, + "summary": "Holding" + }, + "security_posture": { + "score": 0.82, + "summary": "Healthy" + }, + "portfolio_fit": { + "score": 0.86, + "summary": "Strong" + } + }, + "security_posture": { + "label": "healthy", + "score": 0.82 + }, + "hotspots": [ + { + "title": "Keep release notes fresh", + "severity": 0.21, + "category": "finish-line" + } + ], + "action_candidates": [ + { + "title": "Publish the next maintenance release" + } + ], + "analyzer_results": [ + { + "dimension": "testing", + "score": 0.9, + "findings": [ + "Healthy test coverage" + ] + }, + { + "dimension": "documentation", + "score": 0.85, + "findings": [ + "README is strong" + ] + } + ], + "score_explanation": { + "repo": "RepoA", + "top_positive_drivers": [ + "Testing (0.90)", + "Documentation (0.85)", + "Security (0.82)" + ], + "top_negative_drivers": [ + "Security (0.82)", + "Documentation (0.85)", + "Testing (0.90)" + ], + "next_tier_gap_summary": "Already at shipped tier; focus on protecting momentum and preventing drift.", + "next_best_action": "Publish the next maintenance release", + "next_best_action_rationale": null + } + }, + { + "metadata": { + "name": "RepoB", + "full_name": "sample-user/RepoB", + "html_url": "https://github.com/sample-user/RepoB", + "description": "A functional project with follow-through left to do.", + "language": "TypeScript", + "private": false, + "archived": false, + "stars": 5, + "topics": [ + "typescript", + "web" + ] + }, + "overall_score": 0.62, + "interest_score": 0.37, + "grade": "C", + "completeness_tier": "functional", + "badges": [], + "flags": [ + "release-checklist-open" + ], + "lenses": { + "ship_readiness": { + "score": 0.63, + "summary": "Needs work" + }, + "momentum": { + "score": 0.54, + "summary": "Mixed" + }, + "security_posture": { + "score": 0.66, + "summary": "Watch" + }, + "portfolio_fit": { + "score": 0.58, + "summary": "Useful" + } + }, + "security_posture": { + "label": "watch", + "score": 0.66 + }, + "hotspots": [ + { + "title": "Finish release checklist", + "severity": 0.56, + "category": "quality" + } + ], + "action_candidates": [ + { + "title": "Close the reopened release item" + } + ], + "analyzer_results": [ + { + "dimension": "testing", + "score": 0.55, + "findings": [ + "A few test gaps remain" + ] + }, + { + "dimension": "release", + "score": 0.48, + "findings": [ + "Checklist is incomplete" + ] + } + ], + "score_explanation": { + "repo": "RepoB", + "top_positive_drivers": [ + "Security (0.66)", + "Testing (0.55)", + "Release (0.48)" + ], + "top_negative_drivers": [ + "Release (0.48)", + "Testing (0.55)", + "Security (0.66)" + ], + "next_tier_gap_summary": "Needs +0.13 to reach shipped.", + "next_best_action": "Close the reopened release item", + "next_best_action_rationale": null + } + }, + { + "metadata": { + "name": "RepoC", + "full_name": "sample-user/RepoC", + "html_url": "https://github.com/sample-user/RepoC", + "description": "A risky work-in-progress project.", + "language": "Python", + "private": false, + "archived": false, + "stars": 1, + "topics": [ + "python", + "security" + ] + }, + "overall_score": 0.44, + "interest_score": 0.16, + "grade": "D", + "completeness_tier": "wip", + "badges": [], + "flags": [ + "no-tests", + "security-review" + ], + "lenses": { + "ship_readiness": { + "score": 0.42, + "summary": "Thin" + }, + "momentum": { + "score": 0.31, + "summary": "Fragile" + }, + "security_posture": { + "score": 0.28, + "summary": "Risky" + }, + "portfolio_fit": { + "score": 0.36, + "summary": "Questionable" + } + }, + "security_posture": { + "label": "critical", + "score": 0.28 + }, + "hotspots": [ + { + "title": "Security posture needs attention", + "severity": 0.84, + "category": "security-debt" + } + ], + "action_candidates": [ + { + "title": "Preview governance controls" + } + ], + "analyzer_results": [ + { + "dimension": "security", + "score": 0.22, + "findings": [ + "Security controls are missing" + ] + }, + { + "dimension": "testing", + "score": 0.18, + "findings": [ + "No meaningful tests found" + ] + } + ], + "score_explanation": { + "repo": "RepoC", + "top_positive_drivers": [ + "Security (0.28)", + "Testing (0.18)" + ], + "top_negative_drivers": [ + "Testing (0.18)", + "Security (0.28)" + ], + "next_tier_gap_summary": "Needs +0.31 to reach shipped.", + "next_best_action": "Preview governance controls", + "next_best_action_rationale": null + } + } + ] +} \ No newline at end of file diff --git a/output/demo/demo-workbook.xlsx b/output/demo/demo-workbook.xlsx new file mode 100644 index 0000000..c16bfb7 Binary files /dev/null and b/output/demo/demo-workbook.xlsx differ diff --git a/output/demo/operator-control-center-demo.json b/output/demo/operator-control-center-demo.json new file mode 100644 index 0000000..9d89856 --- /dev/null +++ b/output/demo/operator-control-center-demo.json @@ -0,0 +1,134 @@ +{ + "username": "sample-user", + "generated_at": "2026-04-12T12:00:00+00:00", + "report_reference": "", + "watch_state": {}, + "campaign_summary": { + "campaign_type": "security-review", + "label": "Security Review", + "action_count": 1, + "repo_count": 1 + }, + "writeback_preview": { + "sync_mode": "reconcile", + "repos": [ + { + "repo": "RepoC", + "topics": [ + "ghra-call-security-review" + ], + "issue_title": "[Repo Auditor] Security Review", + "notion_action_count": 1 + } + ] + }, + "writeback_results": { + "mode": "apply", + "target": "github", + "results": [ + { + "repo_full_name": "sample-user/RepoC", + "target": "github-issue", + "status": "created", + "url": "https://github.com/sample-user/RepoC/issues/1" + } + ] + }, + "managed_state_drift": [ + { + "repo_full_name": "sample-user/RepoC", + "target": "github-issue", + "drift_state": "managed-issue-edited" + } + ], + "operator_summary": { + "headline": "RepoC still needs security follow-through before the queue will calm down.", + "counts": { + "blocked": 1, + "urgent": 1, + "ready": 1, + "deferred": 1 + }, + "source_run_id": "sample-user:2026-04-12T12:00:00+00:00", + "next_recommended_run_mode": "incremental", + "watch_strategy": "adaptive", + "watch_decision_summary": "The current baseline is still compatible, so incremental watch remains safe for the next run.", + "what_changed": "RepoC drift needs review and RepoB reopened a release checklist item.", + "why_it_matters": "Live drift is still present, so security work should stay ahead of lower-pressure cleanup.", + "what_to_do_next": "Review RepoC first, then close RepoB's reopened checklist item.", + "trend_summary": "Queue pressure is stable but still sticky.", + "follow_through_summary": "One urgent item is still repeating in the recent window.", + "primary_target_reason": "RepoC remains the top target because live drift is still open.", + "closure_guidance": "Clear RepoC's security drift before moving to lower-pressure work.", + "primary_target_trust_policy": "verify-first", + "primary_target_trust_policy_reason": "The recommendation is sound, but recent reopen noise means it should still be reviewed before acting.", + "control_center_reference": "output/demo/operator-control-center-sample-user-2026-04-12.json" + }, + "operator_queue": [ + { + "repo": "RepoC", + "title": "Security drift needs review", + "lane": "blocked", + "lane_label": "Blocked", + "lane_reason": "Governed security drift is still unresolved.", + "kind": "campaign", + "priority": 1, + "summary": "RepoC still needs a governance decision before the security review can settle.", + "recommended_action": "Open the governed control preview and decide whether to apply CodeQL.", + "repo_url": "https://github.com/sample-user/RepoC", + "links": [ + { + "label": "GitHub Issue", + "url": "https://github.com/sample-user/RepoC/issues/1" + } + ] + }, + { + "repo": "RepoB", + "title": "Release checklist reopened", + "lane": "urgent", + "lane_label": "Needs Attention Now", + "lane_reason": "A previously quiet repo reopened a visible checklist item.", + "kind": "review", + "priority": 2, + "summary": "RepoB needs a small release follow-through pass.", + "recommended_action": "Re-run the release checklist and close the missing item.", + "repo_url": "https://github.com/sample-user/RepoB" + }, + { + "repo": "RepoA", + "title": "Protect shipped momentum", + "lane": "ready", + "lane_label": "Ready for Manual Action", + "lane_reason": "RepoA is healthy enough that a small polish task would keep it strong.", + "kind": "maintenance", + "priority": 3, + "summary": "A small polish pass would keep the showcase repo strong.", + "recommended_action": "Tidy the release notes and publish the next small maintenance release.", + "repo_url": "https://github.com/sample-user/RepoA" + }, + { + "repo": "RepoA", + "title": "Archive old brainstorm notes", + "lane": "deferred", + "lane_label": "Safe to Defer", + "lane_reason": "This is cleanup-only work with no current pressure.", + "kind": "cleanup", + "priority": 4, + "summary": "Backlog cleanup is optional this week.", + "recommended_action": "Leave this alone unless priorities change.", + "repo_url": "https://github.com/sample-user/RepoA" + } + ], + "portfolio_outcomes_summary": {}, + "operator_effectiveness_summary": {}, + "high_pressure_queue_history": [], + "operator_setup_health": {}, + "operator_recent_changes": [], + "review_summary": { + "review_id": "sample-review-1", + "status": "open", + "source_run_id": "sample-user:2026-04-12T12:00:00+00:00" + }, + "preflight_summary": {} +} \ No newline at end of file diff --git a/output/demo/operator-control-center-demo.md b/output/demo/operator-control-center-demo.md new file mode 100644 index 0000000..7389638 --- /dev/null +++ b/output/demo/operator-control-center-demo.md @@ -0,0 +1,43 @@ +# Operator Control Center: sample-user + +*Generated:* 2026-04-12 +*Headline:* RepoC still needs security follow-through before the queue will calm down. + +*Source Run:* `sample-user:2026-04-12T12:00:00+00:00` +*Next Recommended Run:* `incremental` +*Watch Strategy:* `adaptive` +*Watch Decision:* The current baseline is still compatible, so incremental watch remains safe for the next run. +*What Changed:* RepoC drift needs review and RepoB reopened a release checklist item. +*Why It Matters:* Live drift is still present, so security work should stay ahead of lower-pressure cleanup. +*What To Do Next:* Review RepoC first, then close RepoB's reopened checklist item. +*Trend:* Queue pressure is stable but still sticky. +*Follow-Through:* One urgent item is still repeating in the recent window. +*Why This Is The Top Target:* RepoC remains the top target because live drift is still open. +*Closure Guidance:* Clear RepoC's security drift before moving to lower-pressure work. +*Trust Policy:* verify-first — The recommendation is sound, but recent reopen noise means it should still be reviewed before acting. +*Control Center Artifact:* `output/demo/operator-control-center-sample-user-2026-04-12.json` +*Setup Health:* unknown | Errors: 0 | Warnings: 0 + +## Blocked + +- RepoC: Security drift needs review — RepoC still needs a governance decision before the security review can settle. + Why this lane: Governed security drift is still unresolved. + Action: Open the governed control preview and decide whether to apply CodeQL. + +## Needs Attention Now + +- RepoB: Release checklist reopened — RepoB needs a small release follow-through pass. + Why this lane: A previously quiet repo reopened a visible checklist item. + Action: Re-run the release checklist and close the missing item. + +## Ready for Manual Action + +- RepoA: Protect shipped momentum — A small polish pass would keep the showcase repo strong. + Why this lane: RepoA is healthy enough that a small polish task would keep it strong. + Action: Tidy the release notes and publish the next small maintenance release. + +## Safe to Defer + +- RepoA: Archive old brainstorm notes — Backlog cleanup is optional this week. + Why this lane: This is cleanup-only work with no current pressure. + Action: Leave this alone unless priorities change. diff --git a/output/demo/pending-proposals.json b/output/demo/pending-proposals.json new file mode 100644 index 0000000..a00df25 --- /dev/null +++ b/output/demo/pending-proposals.json @@ -0,0 +1,4 @@ +{ + "contract_version": "automation_proposals_v1", + "proposals": [] +} \ No newline at end of file diff --git a/output/demo/portfolio-truth-2026-04-05T120000Z.json b/output/demo/portfolio-truth-2026-04-05T120000Z.json new file mode 100644 index 0000000..17f99f4 --- /dev/null +++ b/output/demo/portfolio-truth-2026-04-05T120000Z.json @@ -0,0 +1,245 @@ +{ + "schema_version": "demo-pcc-v1", + "generated_at": "2026-04-05T12:00:00+00:00", + "workspace_root": "fixtures/demo", + "projects": [ + { + "identity": { + "project_key": "RepoA", + "display_name": "RepoA", + "path": "fixtures/demo/RepoA", + "section_marker": "fixture-demo", + "has_git": true, + "top_level_dir": "fixtures", + "group_key": "demo", + "group_label": "Demo Portfolio", + "section_label": "Fixture Demo" + }, + "declared": { + "operating_path": "fixtures/demo/RepoA", + "category": "demo-product", + "tool_provenance": "Codex", + "lifecycle_state": "shipped", + "purpose": "A polished shipped project.", + "criticality": "demo", + "review_cadence": "weekly", + "intended_disposition": "keep", + "maturity_program": "operator-os-fixture", + "target_maturity": "public-demo", + "automation_eligible": false + }, + "derived": { + "context_quality": "full", + "registry_status": "active", + "attention_state": "active-product", + "stack": [ + "Python" + ], + "context_files": [ + "README.md", + "docs/current-state.md" + ], + "context_file_count": 2, + "primary_context_file": "README.md", + "project_summary_present": true, + "current_state_present": true, + "stack_present": true, + "run_instructions_present": true, + "known_risks_present": false, + "next_recommended_move_present": true, + "last_meaningful_activity_at": "2026-04-05T12:00:00+00:00", + "activity_status": "current", + "has_tests": true, + "has_ci": true, + "has_license": true, + "readme_char_count": 2100, + "release_count": 2 + }, + "risk": { + "risk_tier": "baseline", + "risk_factors": [], + "risk_summary": "No elevated fixture risk.", + "security_risk": false, + "doctor_gap": false, + "context_risk": false, + "path_risk": false + }, + "security": { + "alerts_available": true, + "dependabot_critical": 0, + "dependabot_high": 0, + "dependabot_medium": 0, + "dependabot_low": 0, + "secret_scanning_open": 0, + "code_scanning_critical": 0, + "code_scanning_high": 0 + }, + "advisory": { + "legacy_status": "shipped", + "legacy_context_quality": "full", + "legacy_category": "fixture-demo", + "legacy_tool_provenance": "sample" + } + }, + { + "identity": { + "project_key": "RepoB", + "display_name": "RepoB", + "path": "fixtures/demo/RepoB", + "section_marker": "fixture-demo", + "has_git": true, + "top_level_dir": "fixtures", + "group_key": "demo", + "group_label": "Demo Portfolio", + "section_label": "Fixture Demo" + }, + "declared": { + "operating_path": "fixtures/demo/RepoB", + "category": "demo-support", + "tool_provenance": "Claude Code", + "lifecycle_state": "functional", + "purpose": "A functional project with follow-through left to do.", + "criticality": "demo", + "review_cadence": "weekly", + "intended_disposition": "keep", + "maturity_program": "operator-os-fixture", + "target_maturity": "public-demo", + "automation_eligible": false + }, + "derived": { + "context_quality": "minimum-viable", + "registry_status": "recent", + "attention_state": "decision-needed", + "stack": [ + "TypeScript" + ], + "context_files": [ + "README.md", + "docs/current-state.md" + ], + "context_file_count": 2, + "primary_context_file": "README.md", + "project_summary_present": true, + "current_state_present": true, + "stack_present": true, + "run_instructions_present": false, + "known_risks_present": false, + "next_recommended_move_present": true, + "last_meaningful_activity_at": "2026-04-05T12:00:00+00:00", + "activity_status": "current", + "has_tests": true, + "has_ci": false, + "has_license": true, + "readme_char_count": 1800, + "release_count": 0 + }, + "risk": { + "risk_tier": "moderate", + "risk_factors": [ + "open high/critical alerts" + ], + "risk_summary": "Security follow-through needs operator review.", + "security_risk": true, + "doctor_gap": false, + "context_risk": true, + "path_risk": false + }, + "security": { + "alerts_available": true, + "dependabot_critical": 0, + "dependabot_high": 1, + "dependabot_medium": 2, + "dependabot_low": 1, + "secret_scanning_open": 0, + "code_scanning_critical": 0, + "code_scanning_high": 0 + }, + "advisory": { + "legacy_status": "functional", + "legacy_context_quality": "minimum-viable", + "legacy_category": "fixture-demo", + "legacy_tool_provenance": "sample" + } + }, + { + "identity": { + "project_key": "RepoC", + "display_name": "RepoC", + "path": "fixtures/demo/RepoC", + "section_marker": "fixture-demo", + "has_git": true, + "top_level_dir": "fixtures", + "group_key": "demo", + "group_label": "Demo Portfolio", + "section_label": "Fixture Demo" + }, + "declared": { + "operating_path": "fixtures/demo/RepoC", + "category": "demo-support", + "tool_provenance": "Codex", + "lifecycle_state": "wip", + "purpose": "A risky work-in-progress project.", + "criticality": "demo", + "review_cadence": "weekly", + "intended_disposition": "keep", + "maturity_program": "operator-os-fixture", + "target_maturity": "public-demo", + "automation_eligible": true + }, + "derived": { + "context_quality": "minimum-viable", + "registry_status": "parked", + "attention_state": "decision-needed", + "stack": [ + "Python" + ], + "context_files": [ + "README.md", + "docs/current-state.md" + ], + "context_file_count": 2, + "primary_context_file": "README.md", + "project_summary_present": true, + "current_state_present": false, + "stack_present": true, + "run_instructions_present": true, + "known_risks_present": false, + "next_recommended_move_present": true, + "last_meaningful_activity_at": "2026-04-05T12:00:00+00:00", + "activity_status": "current", + "has_tests": false, + "has_ci": false, + "has_license": true, + "readme_char_count": 1500, + "release_count": 0 + }, + "risk": { + "risk_tier": "moderate", + "risk_factors": [ + "open high/critical alerts" + ], + "risk_summary": "Security follow-through needs operator review.", + "security_risk": true, + "doctor_gap": true, + "context_risk": true, + "path_risk": false + }, + "security": { + "alerts_available": true, + "dependabot_critical": 1, + "dependabot_high": 2, + "dependabot_medium": 1, + "dependabot_low": 0, + "secret_scanning_open": 0, + "code_scanning_critical": 0, + "code_scanning_high": 1 + }, + "advisory": { + "legacy_status": "wip", + "legacy_context_quality": "minimum-viable", + "legacy_category": "fixture-demo", + "legacy_tool_provenance": "sample" + } + } + ] +} \ 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 new file mode 100644 index 0000000..0ea81bf --- /dev/null +++ b/output/demo/portfolio-truth-2026-04-12T120000Z.json @@ -0,0 +1,245 @@ +{ + "schema_version": "demo-pcc-v1", + "generated_at": "2026-04-12T12:00:00+00:00", + "workspace_root": "fixtures/demo", + "projects": [ + { + "identity": { + "project_key": "RepoA", + "display_name": "RepoA", + "path": "fixtures/demo/RepoA", + "section_marker": "fixture-demo", + "has_git": true, + "top_level_dir": "fixtures", + "group_key": "demo", + "group_label": "Demo Portfolio", + "section_label": "Fixture Demo" + }, + "declared": { + "operating_path": "fixtures/demo/RepoA", + "category": "demo-product", + "tool_provenance": "Codex", + "lifecycle_state": "shipped", + "purpose": "A polished shipped project.", + "criticality": "demo", + "review_cadence": "weekly", + "intended_disposition": "keep", + "maturity_program": "operator-os-fixture", + "target_maturity": "public-demo", + "automation_eligible": false + }, + "derived": { + "context_quality": "full", + "registry_status": "active", + "attention_state": "active-product", + "stack": [ + "Python" + ], + "context_files": [ + "README.md", + "docs/current-state.md" + ], + "context_file_count": 2, + "primary_context_file": "README.md", + "project_summary_present": true, + "current_state_present": true, + "stack_present": true, + "run_instructions_present": true, + "known_risks_present": false, + "next_recommended_move_present": true, + "last_meaningful_activity_at": "2026-04-12T12:00:00+00:00", + "activity_status": "current", + "has_tests": true, + "has_ci": true, + "has_license": true, + "readme_char_count": 2100, + "release_count": 2 + }, + "risk": { + "risk_tier": "baseline", + "risk_factors": [], + "risk_summary": "No elevated fixture risk.", + "security_risk": false, + "doctor_gap": false, + "context_risk": false, + "path_risk": false + }, + "security": { + "alerts_available": true, + "dependabot_critical": 0, + "dependabot_high": 0, + "dependabot_medium": 0, + "dependabot_low": 0, + "secret_scanning_open": 0, + "code_scanning_critical": 0, + "code_scanning_high": 0 + }, + "advisory": { + "legacy_status": "shipped", + "legacy_context_quality": "full", + "legacy_category": "fixture-demo", + "legacy_tool_provenance": "sample" + } + }, + { + "identity": { + "project_key": "RepoB", + "display_name": "RepoB", + "path": "fixtures/demo/RepoB", + "section_marker": "fixture-demo", + "has_git": true, + "top_level_dir": "fixtures", + "group_key": "demo", + "group_label": "Demo Portfolio", + "section_label": "Fixture Demo" + }, + "declared": { + "operating_path": "fixtures/demo/RepoB", + "category": "demo-support", + "tool_provenance": "Claude Code", + "lifecycle_state": "functional", + "purpose": "A functional project with follow-through left to do.", + "criticality": "demo", + "review_cadence": "weekly", + "intended_disposition": "keep", + "maturity_program": "operator-os-fixture", + "target_maturity": "public-demo", + "automation_eligible": false + }, + "derived": { + "context_quality": "minimum-viable", + "registry_status": "recent", + "attention_state": "decision-needed", + "stack": [ + "TypeScript" + ], + "context_files": [ + "README.md", + "docs/current-state.md" + ], + "context_file_count": 2, + "primary_context_file": "README.md", + "project_summary_present": true, + "current_state_present": true, + "stack_present": true, + "run_instructions_present": false, + "known_risks_present": true, + "next_recommended_move_present": true, + "last_meaningful_activity_at": "2026-04-12T12:00:00+00:00", + "activity_status": "current", + "has_tests": true, + "has_ci": false, + "has_license": true, + "readme_char_count": 1800, + "release_count": 0 + }, + "risk": { + "risk_tier": "elevated", + "risk_factors": [ + "open high/critical alerts" + ], + "risk_summary": "Security follow-through needs operator review.", + "security_risk": true, + "doctor_gap": false, + "context_risk": true, + "path_risk": false + }, + "security": { + "alerts_available": true, + "dependabot_critical": 0, + "dependabot_high": 1, + "dependabot_medium": 2, + "dependabot_low": 1, + "secret_scanning_open": 0, + "code_scanning_critical": 0, + "code_scanning_high": 0 + }, + "advisory": { + "legacy_status": "functional", + "legacy_context_quality": "minimum-viable", + "legacy_category": "fixture-demo", + "legacy_tool_provenance": "sample" + } + }, + { + "identity": { + "project_key": "RepoC", + "display_name": "RepoC", + "path": "fixtures/demo/RepoC", + "section_marker": "fixture-demo", + "has_git": true, + "top_level_dir": "fixtures", + "group_key": "demo", + "group_label": "Demo Portfolio", + "section_label": "Fixture Demo" + }, + "declared": { + "operating_path": "fixtures/demo/RepoC", + "category": "demo-support", + "tool_provenance": "Codex", + "lifecycle_state": "wip", + "purpose": "A risky work-in-progress project.", + "criticality": "demo", + "review_cadence": "weekly", + "intended_disposition": "keep", + "maturity_program": "operator-os-fixture", + "target_maturity": "public-demo", + "automation_eligible": true + }, + "derived": { + "context_quality": "minimum-viable", + "registry_status": "parked", + "attention_state": "decision-needed", + "stack": [ + "Python" + ], + "context_files": [ + "README.md", + "docs/current-state.md" + ], + "context_file_count": 2, + "primary_context_file": "README.md", + "project_summary_present": true, + "current_state_present": false, + "stack_present": true, + "run_instructions_present": true, + "known_risks_present": true, + "next_recommended_move_present": true, + "last_meaningful_activity_at": "2026-04-12T12:00:00+00:00", + "activity_status": "current", + "has_tests": false, + "has_ci": false, + "has_license": true, + "readme_char_count": 1500, + "release_count": 0 + }, + "risk": { + "risk_tier": "elevated", + "risk_factors": [ + "open high/critical alerts" + ], + "risk_summary": "Security follow-through needs operator review.", + "security_risk": true, + "doctor_gap": true, + "context_risk": true, + "path_risk": false + }, + "security": { + "alerts_available": true, + "dependabot_critical": 1, + "dependabot_high": 2, + "dependabot_medium": 1, + "dependabot_low": 0, + "secret_scanning_open": 0, + "code_scanning_critical": 0, + "code_scanning_high": 1 + }, + "advisory": { + "legacy_status": "wip", + "legacy_context_quality": "minimum-viable", + "legacy_category": "fixture-demo", + "legacy_tool_provenance": "sample" + } + } + ] +} \ No newline at end of file diff --git a/output/demo/portfolio-truth-latest.json b/output/demo/portfolio-truth-latest.json new file mode 100644 index 0000000..0ea81bf --- /dev/null +++ b/output/demo/portfolio-truth-latest.json @@ -0,0 +1,245 @@ +{ + "schema_version": "demo-pcc-v1", + "generated_at": "2026-04-12T12:00:00+00:00", + "workspace_root": "fixtures/demo", + "projects": [ + { + "identity": { + "project_key": "RepoA", + "display_name": "RepoA", + "path": "fixtures/demo/RepoA", + "section_marker": "fixture-demo", + "has_git": true, + "top_level_dir": "fixtures", + "group_key": "demo", + "group_label": "Demo Portfolio", + "section_label": "Fixture Demo" + }, + "declared": { + "operating_path": "fixtures/demo/RepoA", + "category": "demo-product", + "tool_provenance": "Codex", + "lifecycle_state": "shipped", + "purpose": "A polished shipped project.", + "criticality": "demo", + "review_cadence": "weekly", + "intended_disposition": "keep", + "maturity_program": "operator-os-fixture", + "target_maturity": "public-demo", + "automation_eligible": false + }, + "derived": { + "context_quality": "full", + "registry_status": "active", + "attention_state": "active-product", + "stack": [ + "Python" + ], + "context_files": [ + "README.md", + "docs/current-state.md" + ], + "context_file_count": 2, + "primary_context_file": "README.md", + "project_summary_present": true, + "current_state_present": true, + "stack_present": true, + "run_instructions_present": true, + "known_risks_present": false, + "next_recommended_move_present": true, + "last_meaningful_activity_at": "2026-04-12T12:00:00+00:00", + "activity_status": "current", + "has_tests": true, + "has_ci": true, + "has_license": true, + "readme_char_count": 2100, + "release_count": 2 + }, + "risk": { + "risk_tier": "baseline", + "risk_factors": [], + "risk_summary": "No elevated fixture risk.", + "security_risk": false, + "doctor_gap": false, + "context_risk": false, + "path_risk": false + }, + "security": { + "alerts_available": true, + "dependabot_critical": 0, + "dependabot_high": 0, + "dependabot_medium": 0, + "dependabot_low": 0, + "secret_scanning_open": 0, + "code_scanning_critical": 0, + "code_scanning_high": 0 + }, + "advisory": { + "legacy_status": "shipped", + "legacy_context_quality": "full", + "legacy_category": "fixture-demo", + "legacy_tool_provenance": "sample" + } + }, + { + "identity": { + "project_key": "RepoB", + "display_name": "RepoB", + "path": "fixtures/demo/RepoB", + "section_marker": "fixture-demo", + "has_git": true, + "top_level_dir": "fixtures", + "group_key": "demo", + "group_label": "Demo Portfolio", + "section_label": "Fixture Demo" + }, + "declared": { + "operating_path": "fixtures/demo/RepoB", + "category": "demo-support", + "tool_provenance": "Claude Code", + "lifecycle_state": "functional", + "purpose": "A functional project with follow-through left to do.", + "criticality": "demo", + "review_cadence": "weekly", + "intended_disposition": "keep", + "maturity_program": "operator-os-fixture", + "target_maturity": "public-demo", + "automation_eligible": false + }, + "derived": { + "context_quality": "minimum-viable", + "registry_status": "recent", + "attention_state": "decision-needed", + "stack": [ + "TypeScript" + ], + "context_files": [ + "README.md", + "docs/current-state.md" + ], + "context_file_count": 2, + "primary_context_file": "README.md", + "project_summary_present": true, + "current_state_present": true, + "stack_present": true, + "run_instructions_present": false, + "known_risks_present": true, + "next_recommended_move_present": true, + "last_meaningful_activity_at": "2026-04-12T12:00:00+00:00", + "activity_status": "current", + "has_tests": true, + "has_ci": false, + "has_license": true, + "readme_char_count": 1800, + "release_count": 0 + }, + "risk": { + "risk_tier": "elevated", + "risk_factors": [ + "open high/critical alerts" + ], + "risk_summary": "Security follow-through needs operator review.", + "security_risk": true, + "doctor_gap": false, + "context_risk": true, + "path_risk": false + }, + "security": { + "alerts_available": true, + "dependabot_critical": 0, + "dependabot_high": 1, + "dependabot_medium": 2, + "dependabot_low": 1, + "secret_scanning_open": 0, + "code_scanning_critical": 0, + "code_scanning_high": 0 + }, + "advisory": { + "legacy_status": "functional", + "legacy_context_quality": "minimum-viable", + "legacy_category": "fixture-demo", + "legacy_tool_provenance": "sample" + } + }, + { + "identity": { + "project_key": "RepoC", + "display_name": "RepoC", + "path": "fixtures/demo/RepoC", + "section_marker": "fixture-demo", + "has_git": true, + "top_level_dir": "fixtures", + "group_key": "demo", + "group_label": "Demo Portfolio", + "section_label": "Fixture Demo" + }, + "declared": { + "operating_path": "fixtures/demo/RepoC", + "category": "demo-support", + "tool_provenance": "Codex", + "lifecycle_state": "wip", + "purpose": "A risky work-in-progress project.", + "criticality": "demo", + "review_cadence": "weekly", + "intended_disposition": "keep", + "maturity_program": "operator-os-fixture", + "target_maturity": "public-demo", + "automation_eligible": true + }, + "derived": { + "context_quality": "minimum-viable", + "registry_status": "parked", + "attention_state": "decision-needed", + "stack": [ + "Python" + ], + "context_files": [ + "README.md", + "docs/current-state.md" + ], + "context_file_count": 2, + "primary_context_file": "README.md", + "project_summary_present": true, + "current_state_present": false, + "stack_present": true, + "run_instructions_present": true, + "known_risks_present": true, + "next_recommended_move_present": true, + "last_meaningful_activity_at": "2026-04-12T12:00:00+00:00", + "activity_status": "current", + "has_tests": false, + "has_ci": false, + "has_license": true, + "readme_char_count": 1500, + "release_count": 0 + }, + "risk": { + "risk_tier": "elevated", + "risk_factors": [ + "open high/critical alerts" + ], + "risk_summary": "Security follow-through needs operator review.", + "security_risk": true, + "doctor_gap": true, + "context_risk": true, + "path_risk": false + }, + "security": { + "alerts_available": true, + "dependabot_critical": 1, + "dependabot_high": 2, + "dependabot_medium": 1, + "dependabot_low": 0, + "secret_scanning_open": 0, + "code_scanning_critical": 0, + "code_scanning_high": 1 + }, + "advisory": { + "legacy_status": "wip", + "legacy_context_quality": "minimum-viable", + "legacy_category": "fixture-demo", + "legacy_tool_provenance": "sample" + } + } + ] +} \ No newline at end of file diff --git a/output/demo/portfolio-warehouse.db b/output/demo/portfolio-warehouse.db new file mode 100644 index 0000000..cd2f075 Binary files /dev/null and b/output/demo/portfolio-warehouse.db differ diff --git a/output/demo/security-burndown-sample-user-2026-04-12.json b/output/demo/security-burndown-sample-user-2026-04-12.json new file mode 100644 index 0000000..19b598d --- /dev/null +++ b/output/demo/security-burndown-sample-user-2026-04-12.json @@ -0,0 +1,30 @@ +{ + "distinct_advisories": 2, + "total_repo_instances": 3, + "repos_touched": 2, + "entries": [ + { + "package": "demo-runtime", + "ecosystem": "pip", + "severity": "critical", + "ghsa_id": "GHSA-DEMO-0001", + "first_patched_version": "2.0.0", + "affected_repos": [ + "RepoC" + ], + "affected_repo_count": 1 + }, + { + "package": "demo-ui-kit", + "ecosystem": "npm", + "severity": "high", + "ghsa_id": "GHSA-DEMO-0002", + "first_patched_version": "4.1.0", + "affected_repos": [ + "RepoB", + "RepoC" + ], + "affected_repo_count": 2 + } + ] +} \ No newline at end of file diff --git a/output/demo/weekly-command-center-sample-user-2026-04-12.json b/output/demo/weekly-command-center-sample-user-2026-04-12.json new file mode 100644 index 0000000..bbe1696 --- /dev/null +++ b/output/demo/weekly-command-center-sample-user-2026-04-12.json @@ -0,0 +1,69 @@ +{ + "username": "sample-user", + "generated_at": "2026-04-12T12:00:00+00:00", + "headline": "Fixture portfolio has one security-driven next move.", + "decision": "Review RepoC before expanding lower-pressure cleanup.", + "why_this_week": "RepoC carries the only critical fixture alert and demonstrates the operator-approved review lane.", + "next_step": "Open the burndown view, confirm the grouped fix, then record the decision.", + "risk_posture": { + "elevated_count": 2, + "risk_tier_counts": { + "elevated": 2, + "moderate": 0, + "baseline": 1, + "deferred": 0 + }, + "top_elevated": [ + { + "repo": "RepoB", + "risk_tier": "elevated", + "risk_summary": "Security follow-through needs operator review." + }, + { + "repo": "RepoC", + "risk_tier": "elevated", + "risk_summary": "Security follow-through needs operator review." + } + ] + }, + "security_posture": { + "scanned_count": 3, + "repos_with_open_high_critical": 2, + "total_open_critical": 1, + "total_open_high": 3, + "top_alerts": [ + { + "repo": "RepoB", + "risk_tier": "elevated", + "dependabot_critical": 0, + "dependabot_high": 1 + }, + { + "repo": "RepoC", + "risk_tier": "elevated", + "dependabot_critical": 1, + "dependabot_high": 2 + } + ] + }, + "path_attention": [ + { + "repo": "RepoA", + "headline": "Fixture context is intentionally compact.", + "registry_status": "active", + "context_quality": "full" + }, + { + "repo": "RepoB", + "headline": "Fixture context is intentionally compact.", + "registry_status": "recent", + "context_quality": "minimum-viable" + }, + { + "repo": "RepoC", + "headline": "Fixture context is intentionally compact.", + "registry_status": "parked", + "context_quality": "minimum-viable" + } + ] +} \ No newline at end of file diff --git a/scripts/build_demo_artifacts.py b/scripts/build_demo_artifacts.py index e7341a6..444e5f5 100644 --- a/scripts/build_demo_artifacts.py +++ b/scripts/build_demo_artifacts.py @@ -126,6 +126,7 @@ def main() -> None: export_html_dashboard(report_data, OUTPUT_DIR, diff_data=diff_data) export_excel(report_path, OUTPUT_DIR / "demo-workbook.xlsx", diff_data=diff_data, excel_mode="standard") _write_demo_portfolio_truth(report_data) + _write_demo_portfolio_command_center_files(report_data) _write_demo_warehouse(report_data, report_path) control_center_json = OUTPUT_DIR / "operator-control-center-demo.json" @@ -151,34 +152,296 @@ def _parse_generated_at(value: str | None) -> datetime: def _write_demo_portfolio_truth(report_data: dict[str, Any]) -> None: - repos = [] - for audit in report_data.get("audits", []): + payload = _build_pcc_truth_snapshot(report_data, report_data.get("generated_at", "")) + (OUTPUT_DIR / "portfolio-truth-latest.json").write_text(json.dumps(payload, indent=2)) + + +def _risk_tier(audit: dict[str, Any]) -> str: + score = float(audit.get("overall_score", 0)) + posture = (audit.get("security_posture") or {}).get("label", "") + if posture in {"critical", "watch"} or score < 0.50: + return "elevated" + if score < 0.75: + return "moderate" + return "baseline" + + +def _context_quality(audit: dict[str, Any]) -> str: + score = float((audit.get("lenses", {}).get("ship_readiness") or {}).get("score", 0)) + if score >= 0.85: + return "full" + if score >= 0.65: + return "standard" + if score >= 0.40: + return "minimum-viable" + return "boilerplate" + + +def _registry_status(audit: dict[str, Any]) -> str: + tier = audit.get("completeness_tier", "") + if tier == "shipped": + return "active" + if tier == "functional": + return "recent" + if tier == "wip": + return "parked" + return "deferred" + + +def _attention_state(audit: dict[str, Any], risk_tier: str, high_crit: int) -> str: + name = (audit.get("metadata") or {}).get("name", "") + if high_crit or risk_tier == "elevated": + return "decision-needed" + if name == "RepoA": + return "active-product" + return "manual-only" + + +def _security_fields(audit: dict[str, Any]) -> dict[str, Any]: + name = (audit.get("metadata") or {}).get("name", "") + if name == "RepoC": + return { + "alerts_available": True, + "dependabot_critical": 1, + "dependabot_high": 2, + "dependabot_medium": 1, + "dependabot_low": 0, + "secret_scanning_open": 0, + "code_scanning_critical": 0, + "code_scanning_high": 1, + } + if name == "RepoB": + return { + "alerts_available": True, + "dependabot_critical": 0, + "dependabot_high": 1, + "dependabot_medium": 2, + "dependabot_low": 1, + "secret_scanning_open": 0, + "code_scanning_critical": 0, + "code_scanning_high": 0, + } + return { + "alerts_available": True, + "dependabot_critical": 0, + "dependabot_high": 0, + "dependabot_medium": 0, + "dependabot_low": 0, + "secret_scanning_open": 0, + "code_scanning_critical": 0, + "code_scanning_high": 0, + } + + +def _build_pcc_truth_snapshot( + report_data: dict[str, Any], + generated_at: str, + *, + risk_shift: int = 0, +) -> dict[str, Any]: + projects = [] + for index, audit in enumerate(report_data.get("audits", []), start=1): metadata = audit.get("metadata", {}) repo_name = metadata.get("name", "") if not repo_name: continue - risk_score = round((1.0 - float(audit.get("overall_score", 0))) * 100, 1) - ship_readiness = (audit.get("lenses", {}).get("ship_readiness") or {}).get("score", 0) - repos.append( + language = metadata.get("language") or "Unknown" + risk_tier = _risk_tier(audit) + if risk_shift < 0 and risk_tier == "elevated": + risk_tier = "moderate" + security = _security_fields(audit) + high_crit = security["dependabot_critical"] + security["dependabot_high"] + attention_state = _attention_state(audit, risk_tier, high_crit) + projects.append( { - "name": repo_name, - "full_name": metadata.get("full_name", repo_name), - "language": metadata.get("language") or "Unknown", - "tier": audit.get("completeness_tier", ""), - "total_score": round(float(audit.get("overall_score", 0)) * 100, 1), - "risk_score": risk_score, - "completeness_score": round(float(ship_readiness) * 100, 1), - "interest_score": round(float(audit.get("interest_score", 0)) * 100, 1), + "identity": { + "project_key": repo_name, + "display_name": repo_name, + "path": f"fixtures/demo/{repo_name}", + "section_marker": "fixture-demo", + "has_git": True, + "top_level_dir": "fixtures", + "group_key": "demo", + "group_label": "Demo Portfolio", + "section_label": "Fixture Demo", + }, + "declared": { + "operating_path": f"fixtures/demo/{repo_name}", + "category": "demo-product" if index == 1 else "demo-support", + "tool_provenance": "Codex" if index % 2 else "Claude Code", + "lifecycle_state": audit.get("completeness_tier", "unknown"), + "purpose": metadata.get("description") or "Fixture project", + "criticality": "demo", + "review_cadence": "weekly", + "intended_disposition": "keep", + "maturity_program": "operator-os-fixture", + "target_maturity": "public-demo", + "automation_eligible": index == 3, + }, + "derived": { + "context_quality": _context_quality(audit), + "registry_status": _registry_status(audit), + "attention_state": attention_state, + "stack": [language], + "context_files": ["README.md", "docs/current-state.md"], + "context_file_count": 2, + "primary_context_file": "README.md", + "project_summary_present": True, + "current_state_present": index != 3, + "stack_present": True, + "run_instructions_present": index != 2, + "known_risks_present": risk_tier == "elevated", + "next_recommended_move_present": True, + "last_meaningful_activity_at": generated_at, + "activity_status": "current", + "has_tests": index != 3, + "has_ci": index == 1, + "has_license": True, + "readme_char_count": 2400 - index * 300, + "release_count": 2 if index == 1 else 0, + }, + "risk": { + "risk_tier": risk_tier, + "risk_factors": [ + "open high/critical alerts" + ] + if high_crit + else [], + "risk_summary": "Security follow-through needs operator review." + if high_crit + else "No elevated fixture risk.", + "security_risk": high_crit > 0, + "doctor_gap": index == 3, + "context_risk": _context_quality(audit) in {"minimum-viable", "boilerplate"}, + "path_risk": False, + }, + "security": security, + "advisory": { + "legacy_status": audit.get("completeness_tier", ""), + "legacy_context_quality": _context_quality(audit), + "legacy_category": "fixture-demo", + "legacy_tool_provenance": "sample", + }, } ) - payload = { - "schema_version": "demo", - "generated_at": report_data.get("generated_at", ""), + return { + "schema_version": "demo-pcc-v1", + "generated_at": generated_at, "workspace_root": "fixtures/demo", - "repos": repos, + "projects": projects, } - (OUTPUT_DIR / "portfolio-truth-latest.json").write_text(json.dumps(payload, indent=2)) + + +def _write_demo_portfolio_command_center_files(report_data: dict[str, Any]) -> None: + generated_at = report_data.get("generated_at", "2026-04-12T12:00:00+00:00") + projects = _build_pcc_truth_snapshot(report_data, generated_at)["projects"] + elevated = [p for p in projects if p["risk"]["risk_tier"] == "elevated"] + open_alerts = [ + p + for p in projects + if p["security"]["dependabot_critical"] + p["security"]["dependabot_high"] > 0 + ] + total_critical = sum(p["security"]["dependabot_critical"] for p in projects) + total_high = sum(p["security"]["dependabot_high"] for p in projects) + digest = { + "username": report_data.get("username", "sample-user"), + "generated_at": generated_at, + "headline": "Fixture portfolio has one security-driven next move.", + "decision": "Review RepoC before expanding lower-pressure cleanup.", + "why_this_week": "RepoC carries the only critical fixture alert and demonstrates the operator-approved review lane.", + "next_step": "Open the burndown view, confirm the grouped fix, then record the decision.", + "risk_posture": { + "elevated_count": len(elevated), + "risk_tier_counts": { + "elevated": len(elevated), + "moderate": len([p for p in projects if p["risk"]["risk_tier"] == "moderate"]), + "baseline": len([p for p in projects if p["risk"]["risk_tier"] == "baseline"]), + "deferred": len([p for p in projects if p["risk"]["risk_tier"] == "deferred"]), + }, + "top_elevated": [ + { + "repo": p["identity"]["project_key"], + "risk_tier": p["risk"]["risk_tier"], + "risk_summary": p["risk"]["risk_summary"], + } + for p in elevated + ], + }, + "security_posture": { + "scanned_count": len(projects), + "repos_with_open_high_critical": len(open_alerts), + "total_open_critical": total_critical, + "total_open_high": total_high, + "top_alerts": [ + { + "repo": p["identity"]["project_key"], + "risk_tier": p["risk"]["risk_tier"], + "dependabot_critical": p["security"]["dependabot_critical"], + "dependabot_high": p["security"]["dependabot_high"], + } + for p in open_alerts + ], + }, + "path_attention": [ + { + "repo": p["identity"]["project_key"], + "headline": "Fixture context is intentionally compact.", + "registry_status": p["derived"]["registry_status"], + "context_quality": p["derived"]["context_quality"], + } + for p in projects + ], + } + (OUTPUT_DIR / "weekly-command-center-sample-user-2026-04-12.json").write_text( + json.dumps(digest, indent=2) + ) + + burndown = { + "distinct_advisories": 2, + "total_repo_instances": 3, + "repos_touched": 2, + "entries": [ + { + "package": "demo-runtime", + "ecosystem": "pip", + "severity": "critical", + "ghsa_id": "GHSA-DEMO-0001", + "first_patched_version": "2.0.0", + "affected_repos": ["RepoC"], + "affected_repo_count": 1, + }, + { + "package": "demo-ui-kit", + "ecosystem": "npm", + "severity": "high", + "ghsa_id": "GHSA-DEMO-0002", + "first_patched_version": "4.1.0", + "affected_repos": ["RepoB", "RepoC"], + "affected_repo_count": 2, + }, + ], + } + (OUTPUT_DIR / "security-burndown-sample-user-2026-04-12.json").write_text( + json.dumps(burndown, indent=2) + ) + + (OUTPUT_DIR / "pending-proposals.json").write_text( + json.dumps({"contract_version": "automation_proposals_v1", "proposals": []}, indent=2) + ) + + previous = _build_pcc_truth_snapshot( + report_data, + "2026-04-05T12:00:00+00:00", + risk_shift=-1, + ) + current = _build_pcc_truth_snapshot(report_data, generated_at) + (OUTPUT_DIR / "portfolio-truth-2026-04-05T120000Z.json").write_text( + json.dumps(previous, indent=2) + ) + (OUTPUT_DIR / "portfolio-truth-2026-04-12T120000Z.json").write_text( + json.dumps(current, indent=2) + ) def _write_demo_warehouse(report_data: dict[str, Any], report_path) -> None: diff --git a/scripts/validate_proof_package.py b/scripts/validate_proof_package.py new file mode 100644 index 0000000..27b1921 --- /dev/null +++ b/scripts/validate_proof_package.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python3 +"""Validate a proof-package.v1 manifest. + +This is deliberately lightweight. It verifies structure and local file +references so proof packages stay easy to inspect without becoming a platform. +""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +from typing import Any + +ALLOWED_STATUSES = {"passed", "failed", "partial", "stale"} +REQUIRED_TOP_LEVEL = { + "schema_version", + "package_id", + "subject", + "producer", + "source_state", + "claims", + "verification", + "safety", + "artifacts", +} +REQUIRED_ARTIFACT_FIELDS = {"id", "kind", "path", "description", "required"} +REQUIRED_CLAIM_FIELDS = {"id", "statement", "status", "evidence"} + + +def _load_manifest(path: Path) -> dict[str, Any]: + with path.open(encoding="utf-8") as handle: + data = json.load(handle) + if not isinstance(data, dict): + raise ValueError("manifest must be a JSON object") + return data + + +def validate_manifest(path: Path) -> list[str]: + errors: list[str] = [] + manifest = _load_manifest(path) + + missing = sorted(REQUIRED_TOP_LEVEL - set(manifest)) + if missing: + errors.append(f"missing top-level fields: {', '.join(missing)}") + + if manifest.get("schema_version") != "proof-package.v1": + errors.append("schema_version must be proof-package.v1") + + artifacts = manifest.get("artifacts") + artifact_ids: set[str] = set() + if not isinstance(artifacts, list) or not artifacts: + errors.append("artifacts must be a non-empty list") + else: + for index, artifact in enumerate(artifacts): + if not isinstance(artifact, dict): + errors.append(f"artifacts[{index}] must be an object") + continue + missing_artifact = sorted(REQUIRED_ARTIFACT_FIELDS - set(artifact)) + if missing_artifact: + errors.append( + f"artifacts[{index}] missing fields: {', '.join(missing_artifact)}" + ) + artifact_id = artifact.get("id") + if isinstance(artifact_id, str): + if artifact_id in artifact_ids: + errors.append(f"duplicate artifact id: {artifact_id}") + artifact_ids.add(artifact_id) + artifact_path = artifact.get("path") + if ( + isinstance(artifact_path, str) + and not artifact.get("external", False) + and artifact.get("required", False) + ): + candidate = Path(artifact_path) + if not candidate.is_absolute(): + candidate = path.parent / candidate + if not candidate.exists(): + errors.append(f"required artifact missing: {artifact_path}") + + claims = manifest.get("claims") + if not isinstance(claims, list) or not claims: + errors.append("claims must be a non-empty list") + else: + for index, claim in enumerate(claims): + if not isinstance(claim, dict): + errors.append(f"claims[{index}] must be an object") + continue + missing_claim = sorted(REQUIRED_CLAIM_FIELDS - set(claim)) + if missing_claim: + errors.append(f"claims[{index}] missing fields: {', '.join(missing_claim)}") + status = claim.get("status") + if status not in ALLOWED_STATUSES: + errors.append(f"claims[{index}] has invalid status: {status}") + evidence = claim.get("evidence") + if not isinstance(evidence, list) or not evidence: + errors.append(f"claims[{index}] evidence must be a non-empty list") + else: + for evidence_id in evidence: + if evidence_id not in artifact_ids: + errors.append( + f"claims[{index}] references unknown artifact: {evidence_id}" + ) + + verification = manifest.get("verification") + if isinstance(verification, dict): + overall = verification.get("overall") + if overall not in ALLOWED_STATUSES: + errors.append(f"verification.overall has invalid status: {overall}") + checks = verification.get("checks") + if not isinstance(checks, list): + errors.append("verification.checks must be a list") + elif "verification" in manifest: + errors.append("verification must be an object") + + return errors + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("manifest", type=Path) + args = parser.parse_args() + + errors = validate_manifest(args.manifest) + if errors: + for error in errors: + print(f"proof package invalid: {error}") + return 1 + print(f"proof package valid: {args.manifest}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/src/portfolio_truth_reconcile.py b/src/portfolio_truth_reconcile.py index 74d2b45..ca50dbf 100644 --- a/src/portfolio_truth_reconcile.py +++ b/src/portfolio_truth_reconcile.py @@ -61,6 +61,7 @@ "derived.last_meaningful_activity_at": ["git", "workspace"], "derived.activity_status": ["derived"], "derived.registry_status": ["derived"], + "derived.attention_state": ["derived"], "derived.path_override": ["normalized"], "derived.path_confidence": ["normalized"], "derived.path_rationale": ["normalized"], @@ -222,6 +223,9 @@ def build_portfolio_truth_snapshot( "registry_status_counts": dict( Counter(project.derived.registry_status for project in projects) ), + "attention_state_counts": dict( + Counter(project.derived.attention_state for project in projects) + ), "duplicate_display_names": _duplicate_display_names(projects), "unresolved_duplicate_display_names": _unresolved_duplicate_display_names(projects), } @@ -458,6 +462,15 @@ def _build_truth_project( security_high_alerts=security.dependabot_high, security_critical_alerts=security.dependabot_critical, ) + attention_state = _attention_state_for( + registry_status=registry_status, + lifecycle_state=declared_values["lifecycle_state"], + operating_path=path_entry.get("operating_path", ""), + intended_disposition=declared_values["intended_disposition"], + category=declared_values["category"], + path_override=path_entry.get("path_override", ""), + risk_entry=risk_entry, + ) declared = DeclaredFields( owner=declared_values["owner"], @@ -482,6 +495,7 @@ def _build_truth_project( } provenance["derived.activity_status"] = {"source": "derived", "detail": activity_status} provenance["derived.registry_status"] = {"source": "derived", "detail": registry_status} + provenance["derived.attention_state"] = {"source": "derived", "detail": attention_state} provenance["derived.stack"] = {"source": "workspace", "detail": ", ".join(raw_project["stack"])} provenance["derived.context_files"] = { "source": "workspace", @@ -543,6 +557,7 @@ def _build_truth_project( last_meaningful_activity_at=last_activity, activity_status=activity_status, registry_status=registry_status, + attention_state=attention_state, path_override=path_entry.get("path_override", ""), path_confidence=path_entry.get("path_confidence", "legacy"), path_rationale=path_entry.get("path_rationale", ""), @@ -723,3 +738,36 @@ def _registry_status_for(activity_status: str) -> str: if activity_status == "stale": return "parked" return activity_status + + +def _attention_state_for( + *, + registry_status: str, + lifecycle_state: str, + operating_path: str, + intended_disposition: str, + category: str, + path_override: str, + risk_entry: dict[str, Any], +) -> str: + if registry_status == "archived" or lifecycle_state == "archived" or operating_path == "archive": + return "archived" + if ( + operating_path == "experiment" + or intended_disposition == "experiment" + or lifecycle_state == "experimental" + ): + return "experiment" + if registry_status == "parked": + return "parked" + if path_override == "investigate" or not operating_path or risk_entry.get("security_risk"): + return "decision-needed" + if registry_status in {"active", "recent"} and operating_path in {"maintain", "finish"}: + if category == "infrastructure": + return "active-infra" + if category == "commercial": + return "active-product" + return "manual-only" + if registry_status in {"active", "recent"}: + return "manual-only" + return "parked" diff --git a/src/portfolio_truth_render.py b/src/portfolio_truth_render.py index 043db19..8b4dae9 100644 --- a/src/portfolio_truth_render.py +++ b/src/portfolio_truth_render.py @@ -96,6 +96,7 @@ def render_portfolio_report_markdown( grouped = _group_projects(snapshot.projects) context_counts = Counter(project.derived.context_quality for project in snapshot.projects) registry_counts = Counter(project.derived.registry_status for project in snapshot.projects) + attention_counts = Counter(project.derived.attention_state for project in snapshot.projects) operating_path_counts = Counter( project.declared.operating_path or "unspecified" for project in snapshot.projects ) @@ -140,13 +141,14 @@ def render_portfolio_report_markdown( "- The truth layer scans the local workspace first, using directory metadata and small allowlisted context files only.", "- Declared ownership, lifecycle, review cadence, category, and tool hints come from `portfolio-catalog.yaml` when present.", "- Local git recency and safe filesystem timestamps drive the derived activity and compatibility status fields.", + "- Registry status describes activity recency; attention state decides whether an item deserves default operator attention.", "- Legacy registry values are treated as migration evidence only; they do not override derived activity or context truth.", "- Optional Notion fields are advisory and are not allowed to replace declared lifecycle or owner data.", "", "## Canonical Portfolio Truth Table", "", - "| Project | Path | Group | Operating Path | Path Status | Lifecycle | Registry Status | Context | Tool | Category | Risk |", - "|---------|------|-------|----------------|-------------|-----------|-----------------|---------|------|----------|------|", + "| Project | Path | Group | Operating Path | Path Status | Lifecycle | Registry Status | Attention State | Context | Tool | Category | Risk |", + "|---------|------|-------|----------------|-------------|-----------|-----------------|-----------------|---------|------|----------|------|", ] for project in snapshot.projects: path_status = project.derived.path_confidence @@ -156,7 +158,8 @@ def render_portfolio_report_markdown( f"| {project.identity.display_name} | `{project.identity.path}` | {project.identity.section_marker} | " f"{project.declared.operating_path or '—'} | {path_status} | " f"{project.declared.lifecycle_state or '—'} | {project.derived.registry_status} | " - f"{project.derived.context_quality} | {project.declared.tool_provenance or 'unknown'} | {project.declared.category or 'unknown'} | {project.risk.risk_tier} |" + f"{project.derived.attention_state} | {project.derived.context_quality} | " + f"{project.declared.tool_provenance or 'unknown'} | {project.declared.category or 'unknown'} | {project.risk.risk_tier} |" ) lines.extend( @@ -166,6 +169,7 @@ def render_portfolio_report_markdown( "", f"- Context coverage: full `{context_counts.get('full', 0)}`, standard `{context_counts.get('standard', 0)}`, minimum-viable `{context_counts.get('minimum-viable', 0)}`, boilerplate `{context_counts.get('boilerplate', 0)}`, none `{context_counts.get('none', 0)}`", f"- Registry status distribution: active `{registry_counts.get('active', 0)}`, recent `{registry_counts.get('recent', 0)}`, parked `{registry_counts.get('parked', 0)}`, archived `{registry_counts.get('archived', 0)}`", + f"- Default attention distribution: active-product `{attention_counts.get('active-product', 0)}`, active-infra `{attention_counts.get('active-infra', 0)}`, decision-needed `{attention_counts.get('decision-needed', 0)}`, manual-only `{attention_counts.get('manual-only', 0)}`, experiment `{attention_counts.get('experiment', 0)}`, parked `{attention_counts.get('parked', 0)}`, archived `{attention_counts.get('archived', 0)}`", f"- Operating path distribution: maintain `{operating_path_counts.get('maintain', 0)}`, finish `{operating_path_counts.get('finish', 0)}`, archive `{operating_path_counts.get('archive', 0)}`, experiment `{operating_path_counts.get('experiment', 0)}`, unspecified `{operating_path_counts.get('unspecified', 0)}`", f"- Investigate overrides currently surfaced: `{override_counts.get('investigate', 0)}`", f"- Risk posture: 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)}`", @@ -185,6 +189,12 @@ def render_portfolio_report_markdown( f" | Parked `{sum(1 for item in projects if item.derived.registry_status == 'parked')}`" f" | Archived `{sum(1 for item in projects if item.derived.registry_status == 'archived')}`" ) + lines.append( + f"- Default attention: active-product `{sum(1 for item in projects if item.derived.attention_state == 'active-product')}`, " + f"active-infra `{sum(1 for item in projects if item.derived.attention_state == 'active-infra')}`, " + f"decision-needed `{sum(1 for item in projects if item.derived.attention_state == 'decision-needed')}`, " + f"non-default `{sum(1 for item in projects if item.derived.attention_state not in {'active-product', 'active-infra', 'decision-needed'})}`" + ) lines.append( f"- Context: full `{sum(1 for item in projects if item.derived.context_quality == 'full')}`, " f"standard `{sum(1 for item in projects if item.derived.context_quality == 'standard')}`, " diff --git a/src/portfolio_truth_types.py b/src/portfolio_truth_types.py index 1610513..3bfa1ad 100644 --- a/src/portfolio_truth_types.py +++ b/src/portfolio_truth_types.py @@ -6,7 +6,7 @@ from pathlib import Path from typing import Any -SCHEMA_VERSION = "0.5.0" +SCHEMA_VERSION = "0.6.0" # The published "latest" portfolio-truth artifact. The producer # (portfolio_truth_publish) writes it; every reader resolves it through @@ -22,6 +22,16 @@ def truth_latest_path(output_dir: Path) -> Path: VALID_CONTEXT_QUALITY = {"full", "standard", "minimum-viable", "boilerplate", "none"} VALID_ACTIVITY_STATUS = {"active", "recent", "stale", "archived"} VALID_REGISTRY_STATUS = {"active", "recent", "parked", "archived"} +VALID_ATTENTION_STATES = { + "active-product", + "active-infra", + "decision-needed", + "parked", + "archived", + "experiment", + "evidence-history", + "manual-only", +} VALID_LIFECYCLE_STATES = {"active", "maintenance", "dormant", "experimental", "archived"} VALID_CATEGORY_TAGS = { "commercial", @@ -105,6 +115,7 @@ class DerivedFields: last_meaningful_activity_at: datetime | None = None activity_status: str = "stale" registry_status: str = "parked" + attention_state: str = "parked" path_override: str = "" path_confidence: str = "legacy" path_rationale: str = "" diff --git a/src/portfolio_truth_validate.py b/src/portfolio_truth_validate.py index fbd1943..7747a87 100644 --- a/src/portfolio_truth_validate.py +++ b/src/portfolio_truth_validate.py @@ -12,6 +12,7 @@ from src.portfolio_truth_types import ( SCHEMA_VERSION, VALID_ACTIVITY_STATUS, + VALID_ATTENTION_STATES, VALID_CONTEXT_QUALITY, VALID_DOCTOR_STANDARDS, VALID_LIFECYCLE_STATES, @@ -49,6 +50,10 @@ def validate_truth_snapshot(snapshot: PortfolioTruthSnapshot) -> None: raise ValueError( f"Invalid registry status for {key}: {project.derived.registry_status}" ) + if project.derived.attention_state not in VALID_ATTENTION_STATES: + raise ValueError( + f"Invalid attention state for {key}: {project.derived.attention_state}" + ) completeness_flags = ( project.derived.project_summary_present, project.derived.current_state_present, diff --git a/src/weekly_command_center.py b/src/weekly_command_center.py index c08c7ec..d9f481b 100644 --- a/src/weekly_command_center.py +++ b/src/weekly_command_center.py @@ -193,7 +193,7 @@ def render_weekly_command_center_markdown(digest: dict[str, Any]) -> str: f"- Next Step: {_safe_text(digest.get('next_step'))}", f"- Decision Quality: `{_safe_text(decision_quality.get('status'))}` — {_safe_text(decision_quality.get('summary'))}", f"- Operating Paths: {_safe_text(digest.get('operating_paths_summary')) or 'No operating-path summary is recorded yet.'}", - f"- Portfolio Truth: {portfolio_truth.get('project_count', 0)} projects, {portfolio_truth.get('active_project_count', 0)} active, {portfolio_truth.get('investigate_override_count', 0)} with investigate override, {portfolio_truth.get('low_confidence_path_count', 0)} low-confidence paths", + f"- Portfolio Truth: {portfolio_truth.get('project_count', 0)} projects, {portfolio_truth.get('active_project_count', 0)} active registry entries, {portfolio_truth.get('default_attention_count', 0)} default attention, {portfolio_truth.get('decision_needed_count', 0)} decision-needed", f"- Risk Posture: {risk_posture.get('elevated_count', 0)} elevated, {tier_counts.get('moderate', 0)} moderate, {tier_counts.get('baseline', 0)} baseline", f"- Security Posture: {security_posture.get('scanned_count', 0)} scanned, {security_posture.get('repos_with_open_high_critical', 0)} with open high/critical Dependabot alerts ({security_posture.get('total_open_critical', 0)} critical, {security_posture.get('total_open_high', 0)} high)", "", @@ -206,7 +206,7 @@ def render_weekly_command_center_markdown(digest: dict[str, Any]) -> str: else: for item in path_attention: lines.append( - f"- {item['repo']} — {item['headline']} ({item['registry_status']}, {item['context_quality']} context)" + f"- {item['repo']} — {item['headline']} ({item['attention_state']}, {item['registry_status']} registry, {item['context_quality']} context)" ) lines.extend(["", "## Automation Candidates"]) @@ -285,6 +285,7 @@ def _build_truth_summary(portfolio_truth: dict[str, Any]) -> dict[str, Any]: path_counts: dict[str, int] = {} override_counts: dict[str, int] = {} risk_tier_counts: dict[str, int] = {} + attention_state_counts: dict[str, int] = {} active_project_count = 0 low_confidence_path_count = 0 @@ -298,6 +299,8 @@ def _build_truth_summary(portfolio_truth: dict[str, Any]) -> dict[str, Any]: path_counts[operating_path] = path_counts.get(operating_path, 0) + 1 override = _safe_text(derived.get("path_override")) or "none" override_counts[override] = override_counts.get(override, 0) + 1 + attention_state = _safe_text(derived.get("attention_state")) or "manual-only" + attention_state_counts[attention_state] = attention_state_counts.get(attention_state, 0) + 1 tier = _safe_text(risk.get("risk_tier")) or "baseline" risk_tier_counts[tier] = risk_tier_counts.get(tier, 0) + 1 if _safe_text(derived.get("registry_status")) == "active": @@ -311,8 +314,14 @@ def _build_truth_summary(portfolio_truth: dict[str, Any]) -> dict[str, Any]: "context_quality_counts": context_counts, "operating_path_counts": path_counts, "path_override_counts": override_counts, + "attention_state_counts": attention_state_counts, "risk_tier_counts": risk_tier_counts, "elevated_risk_count": risk_tier_counts.get("elevated", 0), + "default_attention_count": sum( + attention_state_counts.get(state, 0) + for state in ("active-product", "active-infra", "decision-needed") + ), + "decision_needed_count": attention_state_counts.get("decision-needed", 0), "low_confidence_path_count": low_confidence_path_count, "investigate_override_count": override_counts.get("investigate", 0), } @@ -321,7 +330,7 @@ def _build_truth_summary(portfolio_truth: dict[str, Any]) -> dict[str, Any]: def _build_path_attention_items(portfolio_truth: dict[str, Any]) -> list[dict[str, Any]]: projects = list(portfolio_truth.get("projects") or []) candidates: list[dict[str, Any]] = [] - status_rank = {"active": 0, "candidate": 1, "parked": 2, "archived": 3} + status_rank = {"decision-needed": 0, "active-product": 1, "active-infra": 2} context_rank = {"none": 0, "boilerplate": 1, "minimum-viable": 2, "standard": 3, "full": 4} for project in projects: @@ -329,7 +338,8 @@ def _build_path_attention_items(portfolio_truth: dict[str, Any]) -> list[dict[st declared = _mapping(project.get("declared")) derived = _mapping(project.get("derived")) registry_status = _safe_text(derived.get("registry_status")) - if registry_status not in {"active", "candidate"}: + attention_state = _safe_text(derived.get("attention_state")) + if attention_state != "decision-needed": continue operating_path = _safe_text(declared.get("operating_path")) override = _safe_text(derived.get("path_override")) @@ -348,6 +358,7 @@ def _build_path_attention_items(portfolio_truth: dict[str, Any]) -> list[dict[st { "repo": _safe_text(identity.get("display_name")) or "Repo", "headline": headline, + "attention_state": attention_state, "registry_status": registry_status or "unknown", "context_quality": context_quality, "path_confidence": _safe_text(derived.get("path_confidence")) or "legacy", @@ -359,7 +370,7 @@ def _build_path_attention_items(portfolio_truth: dict[str, Any]) -> list[dict[st candidates.sort( key=lambda item: ( - status_rank.get(item["registry_status"], 9), + status_rank.get(item["attention_state"], 9), 0 if item["operating_path"] == "unspecified" else 1, 0 if item["path_confidence"] == "low" else 1, context_rank.get(item["context_quality"], 9), diff --git a/tests/fixtures/proof-packages/valid/evidence/example.txt b/tests/fixtures/proof-packages/valid/evidence/example.txt new file mode 100644 index 0000000..d70d386 --- /dev/null +++ b/tests/fixtures/proof-packages/valid/evidence/example.txt @@ -0,0 +1 @@ +fixture evidence diff --git a/tests/fixtures/proof-packages/valid/proof-package.json b/tests/fixtures/proof-packages/valid/proof-package.json new file mode 100644 index 0000000..67c545e --- /dev/null +++ b/tests/fixtures/proof-packages/valid/proof-package.json @@ -0,0 +1,57 @@ +{ + "schema_version": "proof-package.v1", + "package_id": "fixture-valid-proof-package", + "subject": { + "repo": "FixtureRepo", + "lane": "demo-proof", + "claim": "The fixture demo proof is structurally valid." + }, + "producer": { + "repo": "FixtureRepo", + "mode": "demo", + "commands": [ + "fixture demo" + ] + }, + "source_state": { + "generated_at": "2026-06-07T00:00:00Z", + "git_branch": "main", + "git_status": "clean", + "freshness_window_hours": 24 + }, + "claims": [ + { + "id": "fixture-evidence-present", + "statement": "The required fixture evidence file exists.", + "status": "passed", + "evidence": [ + "fixture-evidence" + ] + } + ], + "verification": { + "overall": "passed", + "checks": [ + { + "name": "manifest structural validation", + "status": "passed" + } + ], + "missing_receipts": [], + "known_gaps": [] + }, + "safety": { + "redaction": "none", + "secrets_checked": true, + "live_write_performed": false + }, + "artifacts": [ + { + "id": "fixture-evidence", + "kind": "evidence", + "path": "evidence/example.txt", + "description": "Tiny fixture evidence file.", + "required": true + } + ] +} diff --git a/tests/test_portfolio_truth.py b/tests/test_portfolio_truth.py index 407dabc..c74cf8b 100644 --- a/tests/test_portfolio_truth.py +++ b/tests/test_portfolio_truth.py @@ -229,6 +229,7 @@ def test_truth_snapshot_respects_declared_and_derived_fields( assert alpha.declared.category == "commercial" assert alpha.derived.context_quality == "full" assert alpha.derived.registry_status == "active" + assert alpha.derived.attention_state == "active-product" assert alpha.derived.primary_context_file == "CLAUDE.md" assert alpha.derived.project_summary_present is True assert alpha.derived.next_recommended_move_present is True @@ -242,11 +243,91 @@ def test_truth_snapshot_respects_declared_and_derived_fields( assert beta.declared.category == "it-work" assert beta.derived.context_quality == "boilerplate" assert beta.derived.registry_status == "parked" + assert beta.derived.attention_state == "parked" assert gamma.identity.section_marker == "iOS Projects" assert gamma.derived.stack == ["Swift"] - assert result.snapshot.schema_version == "0.5.0" + assert result.snapshot.schema_version == "0.6.0" + assert result.snapshot.source_summary["attention_state_counts"]["active-product"] == 1 + assert result.snapshot.source_summary["attention_state_counts"]["parked"] == 1 + + +def test_attention_state_classifier_separates_activity_from_operator_attention() -> None: + from src.portfolio_truth_reconcile import _attention_state_for + + assert ( + _attention_state_for( + registry_status="active", + lifecycle_state="active", + operating_path="maintain", + intended_disposition="maintain", + category="commercial", + path_override="", + risk_entry={"security_risk": False}, + ) + == "active-product" + ) + assert ( + _attention_state_for( + registry_status="active", + lifecycle_state="active", + operating_path="maintain", + intended_disposition="maintain", + category="infrastructure", + path_override="", + risk_entry={"security_risk": False}, + ) + == "active-infra" + ) + assert ( + _attention_state_for( + registry_status="active", + lifecycle_state="active", + operating_path="maintain", + intended_disposition="maintain", + category="vanity", + path_override="investigate", + risk_entry={"security_risk": False}, + ) + == "decision-needed" + ) + assert ( + _attention_state_for( + registry_status="active", + lifecycle_state="active", + operating_path="maintain", + intended_disposition="maintain", + category="fun", + path_override="", + risk_entry={"security_risk": False}, + ) + == "manual-only" + ) + assert ( + _attention_state_for( + registry_status="active", + lifecycle_state="active", + operating_path="experiment", + intended_disposition="experiment", + category="vanity", + path_override="investigate", + risk_entry={"security_risk": True}, + ) + == "experiment" + ) + assert ( + _attention_state_for( + registry_status="archived", + lifecycle_state="archived", + operating_path="archive", + intended_disposition="archive", + category="commercial", + path_override="investigate", + risk_entry={"security_risk": True}, + ) + == "archived" + ) def test_build_security_fields_maps_ghas_entry() -> None: diff --git a/tests/test_validate_proof_package.py b/tests/test_validate_proof_package.py new file mode 100644 index 0000000..5f8ec2e --- /dev/null +++ b/tests/test_validate_proof_package.py @@ -0,0 +1,48 @@ +import json +from pathlib import Path + +from scripts.validate_proof_package import validate_manifest + + +def test_valid_proof_package_fixture() -> None: + manifest = Path("tests/fixtures/proof-packages/valid/proof-package.json") + + assert validate_manifest(manifest) == [] + + +def test_missing_required_artifact_is_reported(tmp_path: Path) -> None: + manifest = { + "schema_version": "proof-package.v1", + "package_id": "missing-artifact", + "subject": {"repo": "Example", "lane": "demo", "claim": "Demo works"}, + "producer": {"repo": "Example", "mode": "demo", "commands": []}, + "source_state": {"generated_at": "2026-06-07T00:00:00Z"}, + "claims": [ + { + "id": "claim-1", + "statement": "Required evidence exists", + "status": "passed", + "evidence": ["missing-file"], + } + ], + "verification": { + "overall": "passed", + "checks": [], + "missing_receipts": [], + "known_gaps": [], + }, + "safety": {"redaction": "none", "secrets_checked": True, "live_write_performed": False}, + "artifacts": [ + { + "id": "missing-file", + "kind": "receipt", + "path": "receipts/missing.json", + "description": "Missing required receipt", + "required": True, + } + ], + } + path = tmp_path / "proof-package.json" + path.write_text(json.dumps(manifest), encoding="utf-8") + + assert validate_manifest(path) == ["required artifact missing: receipts/missing.json"] diff --git a/tests/test_weekly_command_center.py b/tests/test_weekly_command_center.py index fe30078..8386e00 100644 --- a/tests/test_weekly_command_center.py +++ b/tests/test_weekly_command_center.py @@ -14,6 +14,7 @@ def _make_portfolio_truth() -> dict: "declared": {"operating_path": "maintain"}, "derived": { "registry_status": "active", + "attention_state": "decision-needed", "activity_status": "active", "path_override": "investigate", "path_confidence": "low", @@ -34,6 +35,7 @@ def _make_portfolio_truth() -> dict: "declared": {"operating_path": ""}, "derived": { "registry_status": "active", + "attention_state": "decision-needed", "activity_status": "active", "path_override": "investigate", "path_confidence": "low", @@ -49,11 +51,33 @@ def _make_portfolio_truth() -> dict: "path_risk": True, }, }, + { + "identity": {"display_name": "QuietActive"}, + "declared": {"operating_path": "maintain"}, + "derived": { + "registry_status": "active", + "attention_state": "manual-only", + "activity_status": "active", + "path_override": "", + "path_confidence": "high", + "context_quality": "standard", + "path_rationale": "Active registry entry, but not default operator attention.", + }, + "risk": { + "risk_tier": "baseline", + "risk_factors": [], + "risk_summary": "No current attention decision.", + "doctor_gap": False, + "context_risk": False, + "path_risk": False, + }, + }, { "identity": {"display_name": "ArchiveMe"}, "declared": {"operating_path": "archive"}, "derived": { "registry_status": "archived", + "attention_state": "archived", "activity_status": "stale", "path_override": "", "path_confidence": "high", @@ -116,15 +140,22 @@ def test_build_weekly_command_center_digest_surfaces_truth_and_guardrails() -> N assert digest["contract_version"] == "weekly_command_center_digest_v1" assert digest["authority_cap"] == "bounded-automation" assert digest["decision_quality"]["status"] == "needs-skepticism" - assert digest["portfolio_truth"]["project_count"] == 3 + assert digest["portfolio_truth"]["project_count"] == 4 + assert digest["portfolio_truth"]["active_project_count"] == 3 + assert digest["portfolio_truth"]["default_attention_count"] == 2 + assert digest["portfolio_truth"]["decision_needed_count"] == 2 assert digest["portfolio_truth"]["investigate_override_count"] == 2 + assert digest["portfolio_truth"]["attention_state_counts"]["manual-only"] == 1 assert digest["path_attention"][0]["repo"] == "JobCommandCenter" assert digest["path_attention"][0]["headline"] == "Unspecified stable path" + assert all(item["attention_state"] == "decision-needed" for item in digest["path_attention"]) + assert "QuietActive" not in {item["repo"] for item in digest["path_attention"]} assert digest["report_only_guardrail"].startswith("This digest is descriptive only.") # Risk posture assertions assert digest["risk_posture"]["elevated_count"] == 2 assert digest["portfolio_truth"]["risk_tier_counts"]["elevated"] == 2 + assert digest["portfolio_truth"]["risk_tier_counts"]["baseline"] == 1 assert digest["portfolio_truth"]["risk_tier_counts"]["deferred"] == 1 rendered_md = render_weekly_command_center_markdown(digest)