diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7802779..ed0f85f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -299,13 +299,10 @@ jobs: bash scripts/run_governance_py.sh scripts/check_ci_supply_chain_policy.py bash scripts/run_governance_py.sh scripts/check_docs_navigation_registry.py bash scripts/run_governance_py.sh scripts/check_docs_manual_fact_boundary.py + bash scripts/run_governance_py.sh scripts/check_frontdoor_contract.py + bash scripts/run_governance_py.sh scripts/check_storefront_proof_assets.py bash scripts/run_governance_py.sh scripts/check_docs_render_freshness.py bash scripts/check_gitignore_hygiene.sh - bash scripts/check_repo_hygiene.sh - if [[ "${GITHUB_EVENT_NAME}" == "pull_request" || "${GITHUB_EVENT_NAME}" == "push" ]]; then - bash scripts/hooks/doc_drift_gate.sh - bash scripts/hooks/doc_sync_gate.sh - fi - name: Publish quick-feedback summary if: always() diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3ea1455..efe69a6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ # Host compatibility hook lane: # - Hooks run on host by design. -# - Release-grade canonical full gate remains container-first: `npm run ci` -> `bash scripts/docker_ci.sh ci`. +# - Release-grade canonical full gate remains strict/manual-first: `npm run ci:strict`. repos: - repo: local hooks: @@ -23,7 +23,7 @@ repos: entry: bash scripts/check_repo_hygiene.sh language: system pass_filenames: false - stages: [pre-commit] + stages: [manual] - id: cortexpilot-public-sensitive-surface-gate name: cortexpilot-public-sensitive-surface-gate @@ -39,7 +39,7 @@ repos: language: system pass_filenames: false always_run: true - stages: [pre-commit] + stages: [manual] - id: cortexpilot-root-semantic-cleanliness name: cortexpilot-root-semantic-cleanliness diff --git a/AGENTS.md b/AGENTS.md index 9f83276..404cae7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -20,6 +20,8 @@ Work in CortexPilot as a contract-first engineering agent: ## Key Commands - bootstrap: `npm run bootstrap` +- local fast CI: `npm run ci` +- local strict CI: `npm run ci:strict` - fast verification: `npm run test:quick` - main local gate: `npm run test` - host safety scan: `npm run scan:host-process-risks` @@ -54,12 +56,6 @@ Work in CortexPilot as a contract-first engineering agent: - current-run builders: `artifact_index/current_run_index`, `cost_profile`, `runner_health`, `slo`, `portal`, `provenance`. - docs and wrappers must not hand-maintain live current-run status; they must point readers back to the checker receipts. - if the current-run source manifest is missing, authoritative current-run reports must fail closed or run only in explicit advisory mode. -- protected upstream/live-smoke receipts are route-exempt on `trusted_pr`, - `untrusted_pr`, and hosted-first `push_main`; those routes must not fail just - because manual closeout/provider credentials are absent -- hosted `push_main` governance closeout also treats `upstream_report`, - `upstream_same_run_report`, and `current_run_consistency` as advisory when - the manifest already marks upstream/live smoke route-exempt diff --git a/CLAUDE.md b/CLAUDE.md index b74cc00..4201cd5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -374,6 +374,8 @@ This file mirrors the root AI entrypoint for tools that prefer `CLAUDE.md`. ## Key Commands - `npm run bootstrap` +- `npm run ci` +- `npm run ci:strict` - `npm run test` - `npm run test:quick` - `npm run scan:host-process-risks` diff --git a/README.md b/README.md index 925ace8..e5d6b3f 100644 --- a/README.md +++ b/README.md @@ -3,43 +3,35 @@ AI Work Command Tower for Codex and Claude Code workflows with Model Context Protocol (MCP)-readable proof, replay, and Workflow Cases. -CortexPilot is a contract-first orchestration repo for Codex / Claude Code -teams that want one operator surface, one governed run path, and one -replayable evidence trail instead of scattered agents, logs, and scripts. +CortexPilot gives Codex / Claude Code teams one governed path from the PM +request to the Workflow Case to Proof & Replay, instead of scattered agents, +logs, and local scripts. CortexPilot is a contract-first multi-agent orchestration repository. -CortexPilot is an AI Work Command Tower built around three product words: -**Command Tower**, **Workflow Cases**, and **Proof & Replay**. +The public story is intentionally narrower than the full monorepo: -The desktop operator surfaces are also being pulled onto the same shared copy -and shared status-presentation substrate, so `Run Detail` and `Overview` do -not drift back into page-local wording contracts. +- **See one proven workflow first** +- **Choose the right adoption path second** +- **Open MCP / API / builder / skills surfaces only after the real job is clear** -The current public story speaks first to Codex / Claude Code teams and second -to broader AI ops / platform teams. The front door should ride the heat around -those ecosystems without pretending CortexPilot is already a hosted operator -service. +Current public boundary: CortexPilot is a repo-backed operator control plane, +not a hosted product, and the shipped MCP surface remains **read-only**. -The public homepage now stays intentionally compressed: start with the first -success path, use the compatibility matrix as the main router, and let the -deeper MCP / integration / skills / builder pages carry the detailed -explanations. - -The dashboard public-home now mirrors that same rule: keep the compatibility -card as the main routing layer, but keep a lighter **Use Cases** side door for -the fastest proof-first walkthrough instead of rebuilding a second routing -grid inside the operator surface. - -The product name stays **CortexPilot**. If `cortexpilot.ai` is later claimed, -treat it as a marketing/front-door domain, not as a product rename. - -[Quickstart](#quickstart) · [Docs](docs/README.md) · [Architecture](docs/architecture/runtime-topology.md) · [Ecosystem Map](https://xiaojiou176-open.github.io/CortexPilot-public/ecosystem/) · [Compatibility Matrix](https://xiaojiou176-open.github.io/CortexPilot-public/compatibility/) · [Agent Starter Kits](https://xiaojiou176-open.github.io/CortexPilot-public/agent-starters/) · [Integration Guide](https://xiaojiou176-open.github.io/CortexPilot-public/integrations/) · [Skills Guide](https://xiaojiou176-open.github.io/CortexPilot-public/skills/) · [AI + MCP + API Surfaces](https://xiaojiou176-open.github.io/CortexPilot-public/ai-surfaces/) · [MCP Quickstart](https://xiaojiou176-open.github.io/CortexPilot-public/mcp/) · [API Quickstart](https://xiaojiou176-open.github.io/CortexPilot-public/api/) · [Builder Quickstart](https://xiaojiou176-open.github.io/CortexPilot-public/builders/) · [Use Cases](https://xiaojiou176-open.github.io/CortexPilot-public/use-cases/) · [Client Entry Points](packages/frontend-api-client/README.md) · [Contract Entry Points](packages/frontend-api-contract/docs/README.md) · [Spec](docs/specs/00_SPEC.md) · [Releases](https://github.com/xiaojiou176-open/CortexPilot-public/releases) +[Quickstart](#quickstart) · [First Proven Workflow](https://xiaojiou176-open.github.io/CortexPilot-public/use-cases/) · [Compatibility Matrix](https://xiaojiou176-open.github.io/CortexPilot-public/compatibility/) · [Docs](docs/README.md) · [Architecture](docs/architecture/runtime-topology.md) · [AI + MCP + API Surfaces](https://xiaojiou176-open.github.io/CortexPilot-public/ai-surfaces/) · [Builder Quickstart](https://xiaojiou176-open.github.io/CortexPilot-public/builders/) · [Releases](https://github.com/xiaojiou176-open/CortexPilot-public/releases) ![CortexPilot studio preview card](docs/assets/storefront/cortexpilot-studio-preview.svg) ![CortexPilot command tower flow](docs/assets/storefront/hero-command-tower.svg) +## Open The Right Door + +| If you're here to... | Open this first | +| --- | --- | +| evaluate the product story | [First Proven Workflow](https://xiaojiou176-open.github.io/CortexPilot-public/use-cases/) | +| choose the right Codex / Claude Code / OpenClaw / MCP / skills / builder path | [Compatibility Matrix](https://xiaojiou176-open.github.io/CortexPilot-public/compatibility/) | +| build on the protocol or package surfaces | [AI + MCP + API Surfaces](https://xiaojiou176-open.github.io/CortexPilot-public/ai-surfaces/) and [Builder Quickstart](https://xiaojiou176-open.github.io/CortexPilot-public/builders/) | + The default public loop is simple: **start one workflow case, watch it move through Command Tower, then inspect Proof & Replay before you trust the outcome**. @@ -55,18 +47,23 @@ paths: | validate the smallest governed path | `CORTEXPILOT_HOST_COMPAT=1 bash scripts/test_quick.sh --no-related` | the quickest repo-side proof path without pretending the full system already ran | | inspect what the system records | open the run list and `.runtime-cache/` after the quick path | a concrete evidence bundle and replay surface, not just a shell success line | +A clean first pass should let you: + +- create one task from the PM surface +- watch that task appear in **Command Tower** +- confirm the linked **Workflow Case** +- inspect **Proof & Replay** before trusting the result + +For the public product story, the current official first proven workflow is +[`news_digest`](https://xiaojiou176-open.github.io/CortexPilot-public/use-cases/). +`topic_brief` and `page_brief` are still public showcase paths, not equally +release-proven baselines. + If this repository is close to your use case, star it to track the first public release, new task templates, and storefront updates. -The integration / skills / MCP / builder pages now also carry more copy-paste -drop-in recipes, so external Codex / Claude Code / OpenClaw teams do not have -to reconstruct the vendored-skill, stdio-MCP, and builder-starter path from -source files alone. - -The host-compatible pre-commit lane now routes repo-owned `scripts/*.py` hooks -through `bash scripts/run_governance_py.sh`, so the quality gate stays -deterministic without leaving repo-local `__pycache__` residue behind after a -clean run. +If you need contributor setup instead of product evaluation, jump to the +[30-minute onboarding guide](docs/runbooks/onboarding-30min.md). ## Why CortexPilot Exists @@ -76,87 +73,21 @@ workflow case, and rerun it without guessing?** This repository combines: -- **Command Tower**: one operator surface for governed AI agents, MCP tools, and live run visibility -- **Workflow Cases**: one stable operating record that ties request, queue, verdict, and linked runs together -- **Proof & Replay**: one place to inspect evidence bundles, compare reruns, and replay failures without guessing -- **Operator surfaces**: use the web dashboard or desktop shell to watch and control the same system -- **Role Contract preview**: intake planning can now expose a resolved role binding summary - (prompt ref, MCP bundle ref, runtime binding, fail-closed posture) before execution starts, - while handoff remains a summary/risk evidence surface instead of rewriting the task contract -- **Role binding receipt**: PM-facing `run_intake(...)` responses and run manifests now - carry a contract-derived `role_binding_summary`, so skills / MCP / runtime bindings stay - readable after execution without pretending that summary has execution authority -- **Role binding read model**: run detail now also exposes a stable - `role_binding_read_model` so persisted bundle/runtime state is readable from - read-only surfaces by projecting `contract.json` without promoting that read - model into execution authority -- **Skills bundle surface**: qualifying delivery roles now resolve - `skills_bundle_ref` from the repo-owned `policies/skills_bundle_registry.json` - surface, so bundle status is no longer a placeholder-only field -- **Workflow case read model**: control-plane workflow reads now expose a - `workflow_case_read_model` that points back to the latest linked run's - persisted `role_binding_summary`, keeping workflow/control-plane reads - stable without inventing a second execution authority -- **Workflow detail read-only projection**: dashboard and desktop Workflow Case - detail views now render that `workflow_case_read_model` directly, so - operators can inspect bundle/runtime posture from the current case surface - while `task_contract` remains the only execution authority -- **Prompt 8 contract convergence**: `docs/api/openapi.cortexpilot.json` plus - generated `@cortexpilot/frontend-api-contract` artifacts now publish - run/workflow route bindings and `RoleBindingReadModel` / - `WorkflowCaseReadModel` shapes from one source instead of relying on a - stale helper contract plus hand-written overlays -- **Run Detail read-only projection**: dashboard and desktop Run Detail now - expose `role_binding_read_model` on their primary operator summary surfaces, - so the persisted binding summary is visible where operators already inspect - run status while `task_contract` still owns execution authority -- **Prompt 9 operator catalog**: `/api/agents` now publishes a registry-backed - role catalog, `/api/contracts` now exposes a normalized contract inspector - record, and dashboard/desktop `Agents` + `Contracts` pages project the same - bundle/runtime truth as read-only operator surfaces without becoming - execution authority or edit controls -- **Prompt 10 role configuration desk**: role defaults now have a repo-owned - control-plane overlay in `policies/role_config_registry.json`, plus - preview/apply API surfaces for `system_prompt_ref`, `skills_bundle_ref`, - `mcp_bundle_ref`, and role-level `runtime_binding`; the `Agents` pages act as - the primary control desk while `Contracts` stays inspector-first and - `task_contract` remains the only execution authority -- **Prompt 10 runtime capability truth**: intake preview, run manifests, - operator copilot, `Contracts`, and `Run Detail` now also surface a derived - runtime capability summary (`lane`, `compat_api_mode`, `provider_status`, - `tool_execution`) so runtime/provider posture is readable without pretending - the system already has full tool parity or runtime replaceability -- **Prompt 10 smoke-gate hardening**: staged dashboard smoke builds now keep - their dependency-install log path recreated on each run, and - `apps/dashboard/lib/types.ts` now explicitly re-exports task-pack/runtime - helper values so UI-audit builds fail on product regressions instead of - brittle staging/export drift -- **Prompt 10 clean-room recovery hardening**: empty-runtime recovery now - reinstalls package-local `frontend-api-client` dependencies before it runs - the node smoke bundle, so clean-room verification keeps testing the package - itself instead of failing on missing local installs -- **Prompt 10 clean-room cleanup hardening**: clean-room recovery now runs the - repo-owned workspace-module cleanup before its broad runtime `rm -rf` sweep, - and that cleanup path can now quarantine stubborn `apps/dashboard/node_modules` - residue before deletion, so the recovery lane does not abort on transient - bind-mounted module trees -- **Prompt 10 quick-gate decoupling**: the `cortexpilot_orch.contract` - package now lazy-loads `compiler` / `validator` entrypoints, so Quick - Feedback governance scripts can import `ContractValidator` and schedule - boundary checks without accidentally pulling the runtime-provider stack or - requiring `httpx` on light governance Python paths -- **Quick Feedback light path**: role-config runtime capability summaries now - resolve through a lightweight provider-capability helper, so control-plane - previews keep their fail-closed provider posture without forcing GitHub - quick-path governance checks to import full runtime transport dependencies; - the staged dashboard UI-audit workspace now also copies the required - `packages/frontend-*` sources into its temporary root so Next/Turbopack does - not reject out-of-root symlinks during smoke builds, and repeated pnpm - `ERR_PNPM_ENOENT` recovery now escalates from fresh-store retries to a - workspace-local store path instead of repeating the same failing copy route, - while `provider_resolution` keeps a dead-code-clean compatibility export - surface for callers that still import lightweight helper names from the - runtime module +- **Command Tower**: one operator surface for governed AI work, live run visibility, and queue posture +- **Workflow Cases**: one stable operating record that ties request, verdict, proof, and linked runs together +- **Proof & Replay**: one place to inspect evidence bundles, compare reruns, and replay failures before promotion +- **Operator surfaces**: a web dashboard plus a macOS desktop shell for the same control plane +- **Read-only inspection surfaces**: repo-local MCP, API, and contract read models that expose truth without turning mirrors into execution authority +- **Governed boundaries**: fail-closed gates for CI, host safety, repo hygiene, and public-proof honesty + +If you need the deeper bundle/runtime/read-model details, open the focused +entrypoints instead of treating the root README like the whole control-plane +manual: + +- [AI + MCP + API Surfaces](https://xiaojiou176-open.github.io/CortexPilot-public/ai-surfaces/) +- [Builder Quickstart](https://xiaojiou176-open.github.io/CortexPilot-public/builders/) +- [Contract Entry Points](packages/frontend-api-contract/docs/README.md) +- [Spec](docs/specs/00_SPEC.md) ## Quickstart @@ -532,17 +463,20 @@ current private reporting path on the live public repository. An additional verified fallback private channel is not yet published and should not be assumed. -Current repo-side verification entrypoints: +Default local verification path: ```bash -npm run test +npm run ci npm run test:quick -bash scripts/check_repo_hygiene.sh -npm run scan:workflow-security -npm run scan:trivy -npm run security:scan:closeout +npm run test ``` +`npm run ci` is now the hosted-aligned local fast gate. Use +`npm run ci:strict`, `npm run docs:check`, `bash scripts/check_repo_hygiene.sh`, +`npm run scan:workflow-security`, `npm run scan:trivy`, and +`npm run security:scan:closeout` only when you intentionally want the stricter +closeout/manual layers. + `npm run test:quick` now expects the dashboard clean-room install gate to prove `jsdom` itself can load, instead of pinning success to the presence of a specific transitive dependency layout such as `data-urls`. diff --git a/apps/dashboard/README.md b/apps/dashboard/README.md index 4b587ec..879b52c 100644 --- a/apps/dashboard/README.md +++ b/apps/dashboard/README.md @@ -82,7 +82,7 @@ finished consumer product. - The dashboard public-docs resolver still treats `/integrations/`, `/skills/`, and `/compatibility/` as first-class public docs routes so public-docs base overrides do not strand those CTA links on app-local paths. -- The same public-home polish keeps the explicit `Open use-case guide` side +- The same public-home polish keeps the explicit `See first proven workflow` side door routed through the public-docs resolver, so the proof-first walkthrough stays visible without turning the dashboard back into a second full routing matrix. diff --git a/apps/dashboard/next-env.d.ts b/apps/dashboard/next-env.d.ts index 9edff1c..6891d77 100644 --- a/apps/dashboard/next-env.d.ts +++ b/apps/dashboard/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/types/routes.d.ts"; +import "./.next-storefront-final-capture-english-prod/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/dashboard/tests/home_page.test.tsx b/apps/dashboard/tests/home_page.test.tsx index a66b733..1c1fe12 100644 --- a/apps/dashboard/tests/home_page.test.tsx +++ b/apps/dashboard/tests/home_page.test.tsx @@ -60,7 +60,7 @@ describe("dashboard home run-summary clarity", () => { expect(screen.getByRole("heading", { name: "Command Tower for Codex and Claude Code workflows" })).toBeInTheDocument(); expect( - screen.getByText(/Start one workflow case, watch Command Tower, then inspect Proof & Replay\./) + screen.getByText(/See one proven workflow first, then choose the right adoption path\./) ).toBeInTheDocument(); expect(screen.getByRole("link", { name: "Start first task" })).toHaveAttribute("href", "/pm"); expect(screen.getAllByRole("link", { name: /Workflow Cases/ })[0]).toHaveAttribute("href", "/workflows"); @@ -78,8 +78,12 @@ describe("dashboard home run-summary clarity", () => { expect(screen.getByText("Release-proven first run")).toBeInTheDocument(); expect(screen.getAllByRole("link", { name: /news_digest/i })[0]).toHaveAttribute("href", "/pm?template=news_digest"); expect(screen.getByText("Proof state: official public baseline")).toBeInTheDocument(); + expect(screen.getByRole("link", { name: "Open proof pack" })).toHaveAttribute( + "href", + "https://xiaojiou176-open.github.io/CortexPilot-public/use-cases/" + ); expect(screen.getByText("Choose the right adoption path")).toBeInTheDocument(); - expect(screen.getByRole("link", { name: "Open use-case guide" })).toHaveAttribute( + expect(screen.getByRole("link", { name: "See first proven workflow" })).toHaveAttribute( "href", "https://xiaojiou176-open.github.io/CortexPilot-public/use-cases/" ); @@ -118,7 +122,7 @@ describe("dashboard home run-summary clarity", () => { ); expect(screen.getByText("Live Workflow Case gallery")).toBeInTheDocument(); expect(screen.getByRole("link", { name: "Open Workflow Cases" })).toHaveAttribute("href", "/workflows"); - expect(screen.getByRole("link", { name: "Open use-case guide" })).toHaveAttribute( + expect(screen.getByRole("link", { name: "See first proven workflow" })).toHaveAttribute( "href", "https://xiaojiou176-open.github.io/CortexPilot-public/use-cases/" ); @@ -142,7 +146,7 @@ describe("dashboard home run-summary clarity", () => { expect(zh.integrationCards).toHaveLength(en.integrationCards.length); expect(zh.firstTaskGuideSteps).toHaveLength(en.firstTaskGuideSteps.length); expect(en.aiSurfacesActionHref).toBe("/ai-surfaces/"); - expect(en.publicTemplatesActionHref).toBe("/pm"); + expect(en.publicTemplatesActionHref).toBe("/use-cases/"); expect(zh.liveCaseGalleryActionHref).toBe("/workflows"); expect(en.optionalApprovalStep.href).toBe("/god-mode"); expect(zh.builderQuickstartCtaHref).toBe("/builders/"); @@ -165,7 +169,7 @@ describe("dashboard home run-summary clarity", () => { "href", "https://docs.example/cortexpilot/builders/" ); - expect(screen.getByRole("link", { name: "Open use-case guide" })).toHaveAttribute( + expect(screen.getByRole("link", { name: "See first proven workflow" })).toHaveAttribute( "href", "https://docs.example/cortexpilot/use-cases/" ); @@ -225,11 +229,15 @@ describe("dashboard home run-summary clarity", () => { "href", "https://xiaojiou176-open.github.io/CortexPilot-public/ai-surfaces/" ); + expect(screen.getByRole("link", { name: "打开证明包" })).toHaveAttribute( + "href", + "https://xiaojiou176-open.github.io/CortexPilot-public/use-cases/" + ); expect(screen.getByRole("link", { name: "打开 builder 快速入口" })).toHaveAttribute( "href", "https://xiaojiou176-open.github.io/CortexPilot-public/builders/" ); - expect(screen.getByRole("link", { name: "打开 use-case 指南" })).toHaveAttribute( + expect(screen.getByRole("link", { name: "查看首个已证明工作流" })).toHaveAttribute( "href", "https://xiaojiou176-open.github.io/CortexPilot-public/use-cases/" ); diff --git a/apps/dashboard/tsconfig.json b/apps/dashboard/tsconfig.json index 5dbc30f..6ffc183 100644 --- a/apps/dashboard/tsconfig.json +++ b/apps/dashboard/tsconfig.json @@ -504,7 +504,15 @@ ".next-e2e-41709/types/**/*.ts", ".next-e2e-41709/dev/types/**/*.ts", ".next-e2e-52171/types/**/*.ts", - ".next-e2e-52171/dev/types/**/*.ts" + ".next-e2e-52171/dev/types/**/*.ts", + ".next-storefront-capture/types/**/*.ts", + ".next-storefront-capture/dev/types/**/*.ts", + ".next-storefront-final-capture/types/**/*.ts", + ".next-storefront-final-capture/dev/types/**/*.ts", + ".next-storefront-final-capture-english/types/**/*.ts", + ".next-storefront-final-capture-english/dev/types/**/*.ts", + ".next-storefront-final-capture-english-prod/types/**/*.ts", + ".next-storefront-final-capture-english-prod/dev/types/**/*.ts" ], "exclude": [ "node_modules" diff --git a/apps/orchestrator/src/cortexpilot_orch/planning/intake.py b/apps/orchestrator/src/cortexpilot_orch/planning/intake.py index 2e2a523..58c400e 100644 --- a/apps/orchestrator/src/cortexpilot_orch/planning/intake.py +++ b/apps/orchestrator/src/cortexpilot_orch/planning/intake.py @@ -515,6 +515,7 @@ def _apply_intake_contract_overrides( if "search" not in normalized_contract_mcp_tools: normalized_contract_mcp_tools.append("search") contract["mcp_tool_set"] = normalized_contract_mcp_tools + contract.pop("handoff_chain", None) return sync_role_contract(contract) diff --git a/apps/orchestrator/src/cortexpilot_orch/scheduler/execute_task_pipeline.py b/apps/orchestrator/src/cortexpilot_orch/scheduler/execute_task_pipeline.py index baeac10..42588f5 100644 --- a/apps/orchestrator/src/cortexpilot_orch/scheduler/execute_task_pipeline.py +++ b/apps/orchestrator/src/cortexpilot_orch/scheduler/execute_task_pipeline.py @@ -438,6 +438,12 @@ def run_execution_pipeline( if state.failure_reason: return state + assigned_role = agent_role_fn(assigned_agent) + if assigned_role in {"SEARCHER", "RESEARCHER"} and state.search_request is not None: + state.runner_summary = "Search pipeline completed successfully." + state.status = "SUCCESS" + return state + task_execution_pipeline_module.snapshot_worktree = snapshot_worktree_fn task_execution_pipeline_module.validate_reviewer_isolation = validate_reviewer_isolation_fn task_execution_pipeline_module._collect_diff_text = collect_diff_text_fn diff --git a/apps/orchestrator/src/cortexpilot_orch/scheduler/tool_execution_pipeline.py b/apps/orchestrator/src/cortexpilot_orch/scheduler/tool_execution_pipeline.py index e1693a2..4914e16 100644 --- a/apps/orchestrator/src/cortexpilot_orch/scheduler/tool_execution_pipeline.py +++ b/apps/orchestrator/src/cortexpilot_orch/scheduler/tool_execution_pipeline.py @@ -145,6 +145,13 @@ def _write_public_task_result( ) +def _profile_mode_from_policy(policy: dict[str, Any] | None) -> str: + if not isinstance(policy, dict): + return "" + value = str(policy.get("profile_mode") or "").strip().lower() + return value + + def run_search_pipeline( run_id: str, tool_runner: ToolRunner, @@ -203,6 +210,17 @@ def _normalize_provider(raw: Any) -> str: verify_repeat = 1 request_policy = request.get("browser_policy") if isinstance(request.get("browser_policy"), dict) else None + effective_profile_mode = ( + _profile_mode_from_policy(request_policy) + or _profile_mode_from_policy(contract_policy) + ) + if effective_profile_mode == "allow_profile" and parallel > 1: + policy_adjustments["parallel"] = { + "from": parallel, + "to": 1, + "reason": "allow_profile browser sessions are serialized to avoid shared-context flake", + } + parallel = 1 tasks: list[tuple[str, str, dict[str, Any] | None, str]] = [] task_index = 0 diff --git a/apps/orchestrator/tests/test_frontdoor_contract_gate.py b/apps/orchestrator/tests/test_frontdoor_contract_gate.py new file mode 100644 index 0000000..8e3121c --- /dev/null +++ b/apps/orchestrator/tests/test_frontdoor_contract_gate.py @@ -0,0 +1,108 @@ +from __future__ import annotations + +import importlib.util +import sys +from pathlib import Path + + +def _load_gate_module() -> object: + script_path = Path(__file__).resolve().parents[3] / "scripts" / "check_frontdoor_contract.py" + spec = importlib.util.spec_from_file_location("cortexpilot_frontdoor_contract_gate", script_path) + module = importlib.util.module_from_spec(spec) + assert spec and spec.loader + spec.loader.exec_module(module) + return module + + +def _write_frontdoor_fixture(root: Path) -> None: + (root / "docs" / "use-cases").mkdir(parents=True, exist_ok=True) + (root / "docs" / "compatibility").mkdir(parents=True, exist_ok=True) + (root / "docs" / "releases" / "assets").mkdir(parents=True, exist_ok=True) + (root / "docs" / "assets" / "storefront").mkdir(parents=True, exist_ok=True) + + (root / "docs" / "index.html").write_text( + """ + See the first proven workflow + Choose the right adoption path +

repo-backed operator control plane, not a hosted product

+

shipped MCP surface remains read-only

+

news_digest topic_brief page_brief

+ """, + encoding="utf-8", + ) + (root / "docs" / "use-cases" / "index.html").write_text( + """ +

First proven workflow and public proof pack

+

news_digest is the only official release-proven public baseline.

+

topic_brief and page_brief are not yet equally release-proven.

+

What we still do not claim

+ Open proof summary + Open benchmark summary + Open recap asset + Open proof-pack manifest + Open proof-pack index + Open demo-status ledger + """, + encoding="utf-8", + ) + (root / "docs" / "compatibility" / "index.html").write_text( + """ +

One truthful compatibility matrix for modern coding-agent teams.

+

read-only MCP

+ See the first proven workflow + """, + encoding="utf-8", + ) + (root / "docs" / "releases" / "assets" / "news-digest-healthy-proof-2026-03-27.md").write_text("ok\n", encoding="utf-8") + (root / "docs" / "releases" / "assets" / "news-digest-benchmark-summary-2026-03-27.md").write_text("ok\n", encoding="utf-8") + (root / "docs" / "releases" / "assets" / "news-digest-workflow-case-recap-2026-03-27.md").write_text("ok\n", encoding="utf-8") + (root / "docs" / "releases" / "assets" / "news-digest-proof-pack-2026-03-27.json").write_text("{}\n", encoding="utf-8") + (root / "docs" / "assets" / "storefront" / "demo-status.md").write_text("ok\n", encoding="utf-8") + (root / "docs" / "assets" / "storefront" / "proof-pack-index.json").write_text("{}\n", encoding="utf-8") + + +def test_frontdoor_contract_gate_passes_with_required_surfaces(tmp_path: Path, monkeypatch) -> None: + module = _load_gate_module() + _write_frontdoor_fixture(tmp_path) + module.ROOT = tmp_path + module.INDEX_PATH = tmp_path / "docs" / "index.html" + module.USE_CASES_PATH = tmp_path / "docs" / "use-cases" / "index.html" + module.COMPATIBILITY_PATH = tmp_path / "docs" / "compatibility" / "index.html" + module.PROOF_SUMMARY_PATH = tmp_path / "docs" / "releases" / "assets" / "news-digest-healthy-proof-2026-03-27.md" + module.BENCHMARK_SUMMARY_PATH = tmp_path / "docs" / "releases" / "assets" / "news-digest-benchmark-summary-2026-03-27.md" + module.WORKFLOW_RECAP_PATH = tmp_path / "docs" / "releases" / "assets" / "news-digest-workflow-case-recap-2026-03-27.md" + module.PROOF_PACK_MANIFEST_PATH = tmp_path / "docs" / "releases" / "assets" / "news-digest-proof-pack-2026-03-27.json" + module.DEMO_STATUS_PATH = tmp_path / "docs" / "assets" / "storefront" / "demo-status.md" + module.PROOF_PACK_INDEX_PATH = tmp_path / "docs" / "assets" / "storefront" / "proof-pack-index.json" + monkeypatch.setattr(sys, "argv", ["check_frontdoor_contract.py"]) + assert module.main() == 0 + + +def test_frontdoor_contract_gate_fails_when_proof_link_drifts( + tmp_path: Path, capsys, monkeypatch +) -> None: + module = _load_gate_module() + _write_frontdoor_fixture(tmp_path) + use_cases = tmp_path / "docs" / "use-cases" / "index.html" + use_cases.write_text( + use_cases.read_text(encoding="utf-8").replace( + "Open benchmark summary", "Open benchmark bundle" + ), + encoding="utf-8", + ) + module.ROOT = tmp_path + module.INDEX_PATH = tmp_path / "docs" / "index.html" + module.USE_CASES_PATH = use_cases + module.COMPATIBILITY_PATH = tmp_path / "docs" / "compatibility" / "index.html" + module.PROOF_SUMMARY_PATH = tmp_path / "docs" / "releases" / "assets" / "news-digest-healthy-proof-2026-03-27.md" + module.BENCHMARK_SUMMARY_PATH = tmp_path / "docs" / "releases" / "assets" / "news-digest-benchmark-summary-2026-03-27.md" + module.WORKFLOW_RECAP_PATH = tmp_path / "docs" / "releases" / "assets" / "news-digest-workflow-case-recap-2026-03-27.md" + module.PROOF_PACK_MANIFEST_PATH = tmp_path / "docs" / "releases" / "assets" / "news-digest-proof-pack-2026-03-27.json" + module.DEMO_STATUS_PATH = tmp_path / "docs" / "assets" / "storefront" / "demo-status.md" + module.PROOF_PACK_INDEX_PATH = tmp_path / "docs" / "assets" / "storefront" / "proof-pack-index.json" + monkeypatch.setattr(sys, "argv", ["check_frontdoor_contract.py"]) + + rc = module.main() + out = capsys.readouterr().out + assert rc == 1 + assert "Open benchmark summary" in out diff --git a/apps/orchestrator/tests/test_generate_storefront_proof_pack_index.py b/apps/orchestrator/tests/test_generate_storefront_proof_pack_index.py new file mode 100644 index 0000000..a792781 --- /dev/null +++ b/apps/orchestrator/tests/test_generate_storefront_proof_pack_index.py @@ -0,0 +1,84 @@ +from __future__ import annotations + +import importlib.util +import json +from pathlib import Path + + +def _load_generator_module() -> object: + script_path = Path(__file__).resolve().parents[3] / "scripts" / "generate_storefront_proof_pack_index.py" + spec = importlib.util.spec_from_file_location("cortexpilot_generate_storefront_proof_pack_index", script_path) + module = importlib.util.module_from_spec(spec) + assert spec and spec.loader + spec.loader.exec_module(module) + return module + + +def test_generate_storefront_proof_pack_index_builds_assets_from_pack_manifest(tmp_path: Path) -> None: + module = _load_generator_module() + root = tmp_path + (root / "configs").mkdir(parents=True, exist_ok=True) + (root / "docs" / "releases" / "assets").mkdir(parents=True, exist_ok=True) + + registry = { + "schema_version": 1, + "artifact_type": "cortexpilot_storefront_proof_bundle_registry", + "vocabulary_contract": { + "proven_workflow_label": "first proven workflow", + "proof_pack_label": "public proof pack", + "showcase_label": "showcase expansion", + }, + "bundles": [ + { + "bundle_id": "news_digest", + "task_template": "news_digest", + "proof_state": "release_proven", + "claim_scope": "official_first_public_baseline", + "authority_level": "repo_side_public_proof", + "public_entrypoint": "docs/use-cases/index.html", + "pack_manifest": "docs/releases/assets/news-digest-proof-pack-2026-03-27.json", + "safe_public_claims": ["claim"], + "forbidden_claims": ["forbidden"], + "capture_contract": { + "healthy_live_capture_gif_present": False, + "healthy_english_first_public_capture_set_present": False, + "current_tracked_dashboard_captures": "local_degraded_non_english_mixed", + }, + "missing_expected_artifacts": ["healthy_live_capture_gif"], + } + ], + } + pack_manifest = { + "artifact_type": "news_digest_public_proof_pack", + "primary_assets": { + "proof_summary_markdown": "docs/releases/assets/news-digest-healthy-proof-2026-03-27.md", + "proof_summary_json": "docs/releases/assets/news-digest-healthy-proof-summary-2026-03-27.json", + "benchmark_summary_markdown": "docs/releases/assets/news-digest-benchmark-summary-2026-03-27.md", + "benchmark_summary_json": "docs/releases/assets/news-digest-benchmark-summary-2026-03-27.json", + "workflow_case_recap_markdown": "docs/releases/assets/news-digest-workflow-case-recap-2026-03-27.md", + "demo_status_markdown": "docs/assets/storefront/demo-status.md", + }, + "supporting_assets": { + "gemini_proof_screenshot": "docs/releases/assets/news-digest-healthy-proof-gemini-2026-03-27.png", + }, + } + registry_path = root / "configs" / "storefront_proof_bundle_registry.json" + registry_path.write_text(json.dumps(registry, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + manifest_path = root / "docs" / "releases" / "assets" / "news-digest-proof-pack-2026-03-27.json" + manifest_path.write_text(json.dumps(pack_manifest, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + + module.ROOT = root + module.REGISTRY_PATH = registry_path + module.OUTPUT_PATH = root / "docs" / "assets" / "storefront" / "proof-pack-index.json" + + registry_payload = module._load_json(registry_path) + registry_payload["source_registry"] = "configs/storefront_proof_bundle_registry.json" + rendered = module.build_index(registry_payload) + + assert rendered["artifact_type"] == "cortexpilot_public_proof_pack_index" + assert rendered["source_registry"] == "configs/storefront_proof_bundle_registry.json" + news = rendered["bundles"][0] + roles = {item["role"] for item in news["assets"]} + assert "healthy_proof_summary" in roles + assert "benchmark_summary_machine" in roles + assert "gemini_proof_screenshot" in roles diff --git a/apps/orchestrator/tests/test_github_control_plane.py b/apps/orchestrator/tests/test_github_control_plane.py new file mode 100644 index 0000000..3a0cd2c --- /dev/null +++ b/apps/orchestrator/tests/test_github_control_plane.py @@ -0,0 +1,112 @@ +from __future__ import annotations + +import importlib.util +import json +import sys +from pathlib import Path + + +def _load_module() -> object: + script_path = Path(__file__).resolve().parents[3] / "scripts" / "check_github_control_plane.py" + spec = importlib.util.spec_from_file_location("cortexpilot_check_github_control_plane", script_path) + module = importlib.util.module_from_spec(spec) + assert spec and spec.loader + spec.loader.exec_module(module) + return module + + +def _policy_payload() -> dict: + return { + "owner": "example", + "repo": "repo", + "default_branch": "main", + "required_actions_permissions": { + "enabled": True, + "allowed_actions": "all", + "sha_pinning_required": True, + }, + "required_environments": ["owner-approved-sensitive"], + "required_checks": ["Quick Feedback"], + "branch_protection_required": True, + "platform_evidence": { + "private_vulnerability_reporting": {"required": True, "mode": "api"}, + "vulnerability_alerts": {"required": True, "mode": "api"}, + "secret_scanning": {"required": True, "mode": "api"}, + "secret_scanning_push_protection": {"required": True, "mode": "api"}, + "secret_scanning_non_provider_patterns": {"required": True, "mode": "api"}, + "secret_scanning_validity_checks": {"required": True, "mode": "api"}, + "dependabot_config": {"required": True, "mode": "repo_file", "path": ".github/dependabot.yml"}, + "codeql_workflow": {"required": True, "mode": "repo_file", "path": ".github/workflows/codeql.yml"}, + "codeql_config": {"required": True, "mode": "repo_file", "path": ".github/codeql/codeql-config.yml"}, + }, + } + + +def _fake_gh_json(secret_non_provider: str, validity_checks: str): + def _impl(path: str) -> tuple[int, dict]: + if path == "repos/example/repo": + return ( + 0, + { + "default_branch": "main", + "security_and_analysis": { + "secret_scanning": {"status": "enabled"}, + "secret_scanning_push_protection": {"status": "enabled"}, + "secret_scanning_non_provider_patterns": {"status": secret_non_provider}, + "secret_scanning_validity_checks": {"status": validity_checks}, + }, + }, + ) + if path == "repos/example/repo/actions/permissions": + return 0, {"enabled": True, "allowed_actions": "all", "sha_pinning_required": True} + if path == "repos/example/repo/branches/main/protection": + return 0, {"required_status_checks": {"contexts": ["Quick Feedback"]}} + if path == "repos/example/repo/environments": + return 0, {"environments": [{"name": "owner-approved-sensitive"}]} + if path == "repos/example/repo/private-vulnerability-reporting": + return 0, {"enabled": True} + if path == "repos/example/repo/vulnerability-alerts": + return 0, {} + if path == "repos/example/repo/code-scanning/default-setup": + return 0, {} + if path == "repos/example/repo/dependabot/alerts?per_page=1": + return 0, [] + raise AssertionError(path) + + return _impl + + +def test_github_control_plane_requires_secret_scanning_subfeatures(tmp_path: Path, monkeypatch) -> None: + module = _load_module() + policy_path = tmp_path / "policy.json" + output_path = tmp_path / "report.json" + policy_path.write_text(json.dumps(_policy_payload(), ensure_ascii=False, indent=2), encoding="utf-8") + + monkeypatch.setattr(module, "ROOT", tmp_path) + monkeypatch.setattr(module, "_gh_json", _fake_gh_json("disabled", "disabled")) + monkeypatch.setattr(module, "_repo_path_exists", lambda _path: True) + monkeypatch.setattr(sys, "argv", ["check_github_control_plane.py", "--policy", str(policy_path), "--output", str(output_path)]) + + rc = module.main() + report = json.loads(output_path.read_text(encoding="utf-8")) + assert rc == 1 + assert any("secret_scanning_non_provider_patterns drift" in item for item in report["errors"]) + assert any("secret_scanning_validity_checks drift" in item for item in report["errors"]) + + +def test_github_control_plane_accepts_enabled_secret_scanning_subfeatures(tmp_path: Path, monkeypatch) -> None: + module = _load_module() + policy_path = tmp_path / "policy.json" + output_path = tmp_path / "report.json" + policy_path.write_text(json.dumps(_policy_payload(), ensure_ascii=False, indent=2), encoding="utf-8") + + monkeypatch.setattr(module, "ROOT", tmp_path) + monkeypatch.setattr(module, "_gh_json", _fake_gh_json("enabled", "enabled")) + monkeypatch.setattr(module, "_repo_path_exists", lambda _path: True) + monkeypatch.setattr(sys, "argv", ["check_github_control_plane.py", "--policy", str(policy_path), "--output", str(output_path)]) + + rc = module.main() + report = json.loads(output_path.read_text(encoding="utf-8")) + assert rc == 0 + assert report["errors"] == [] + assert report["security_and_analysis"]["secret_scanning_non_provider_patterns"]["status"] == "enabled" diff --git a/apps/orchestrator/tests/test_intake_branch_matrix_extra.py b/apps/orchestrator/tests/test_intake_branch_matrix_extra.py index fb6c1e0..8e52e3c 100644 --- a/apps/orchestrator/tests/test_intake_branch_matrix_extra.py +++ b/apps/orchestrator/tests/test_intake_branch_matrix_extra.py @@ -205,7 +205,7 @@ def test_intake_service_build_contract_artifacts_non_list(tmp_path: Path, monkey contract = service.build_contract(intake_id) assert isinstance(contract, dict) - assert contract.get("handoff_chain", {}).get("enabled") is True + assert "handoff_chain" not in contract artifacts = contract.get("inputs", {}).get("artifacts", []) assert isinstance(artifacts, list) assert any(item.get("name") == "search_requests.json" for item in artifacts) diff --git a/apps/orchestrator/tests/test_intake_helpers_coverage.py b/apps/orchestrator/tests/test_intake_helpers_coverage.py index 160f9d7..75b6fc7 100644 --- a/apps/orchestrator/tests/test_intake_helpers_coverage.py +++ b/apps/orchestrator/tests/test_intake_helpers_coverage.py @@ -426,13 +426,13 @@ def test_intake_service_answer_and_build_contract(monkeypatch, tmp_path: Path) - assert any(item.get("name") == "search_requests.json" for item in artifacts) assert contract["tool_permissions"]["mcp_tools"] == ["codex", "search"] - # PM owner should force handoff chain in compiled contract. + # Search-style compiled contracts should stay direct instead of carrying a PM handoff chain. response_path = service._store._intake_dir(intake_id) / "response.json" response = json.loads(response_path.read_text(encoding="utf-8")) response["plan"]["owner_agent"] = {"role": "PM", "agent_id": "agent-1"} response_path.write_text(json.dumps(response, ensure_ascii=False, indent=2), encoding="utf-8") contract_pm = service.build_contract(intake_id) - assert contract_pm.get("handoff_chain", {}).get("enabled") is True + assert "handoff_chain" not in contract_pm def test_intake_service_auto_run_chain_restores_runner_env(monkeypatch, tmp_path: Path) -> None: diff --git a/apps/orchestrator/tests/test_news_digest_template.py b/apps/orchestrator/tests/test_news_digest_template.py index c0776a0..b236c34 100644 --- a/apps/orchestrator/tests/test_news_digest_template.py +++ b/apps/orchestrator/tests/test_news_digest_template.py @@ -59,6 +59,7 @@ def test_news_digest_intake_builds_contract_artifact(monkeypatch, tmp_path: Path assert contract["template_payload"]["sources"] == ["theverge.com", "techcrunch.com"] assert contract["owner_agent"]["role"] == "TECH_LEAD" assert contract["assigned_agent"]["role"] == "SEARCHER" + assert "handoff_chain" not in contract assert contract["tool_permissions"]["network"] == "allow" assert "search" in contract["tool_permissions"]["mcp_tools"] assert "search" in contract["mcp_tool_set"] @@ -95,6 +96,8 @@ def test_news_digest_result_builder_and_search_payload() -> None: assert digest["status"] == "SUCCESS" assert digest["topic"] == "Seattle AI" assert len(digest["sources"]) == 2 + assert digest["summary"].startswith("Collected 2 public-source result(s)") + assert "Title A, Title B." in digest["summary"] payload = search_payload_helpers.build_search_payload( "run-news", @@ -331,4 +334,5 @@ def run_search(self, query: str, provider: str | None = None, browser_policy=Non assert digest_path.exists() digest_payload = json.loads(digest_path.read_text(encoding="utf-8")) assert digest_payload["status"] == "FAILED" + assert digest_payload["summary"].startswith("The news digest for 'Seattle AI' did not complete successfully.") assert "来源链路失败" in digest_payload["failure_reason_zh"] diff --git a/apps/orchestrator/tests/test_scheduler_helpers.py b/apps/orchestrator/tests/test_scheduler_helpers.py index 8ca3727..385472d 100644 --- a/apps/orchestrator/tests/test_scheduler_helpers.py +++ b/apps/orchestrator/tests/test_scheduler_helpers.py @@ -1,4 +1,5 @@ import json +import threading import time from pathlib import Path @@ -296,6 +297,51 @@ def run_search(self, query: str, provider: str = "chatgpt_web", browser_policy=N assert verify_failing.get("verify_failures") +def test_run_search_pipeline_serializes_allow_profile_browser_sessions(tmp_path: Path, monkeypatch) -> None: + runs_root = tmp_path / "runs" + monkeypatch.setenv("CORTEXPILOT_RUNS_ROOT", str(runs_root)) + store = RunStore(runs_root=runs_root) + run_id = store.create_run("task_search_serialized") + + lock = threading.Lock() + active_calls = 0 + max_concurrent = 0 + + class DummyToolRunner: + def run_search(self, query: str, provider: str = "chatgpt_web", browser_policy=None, policy_audit=None): + nonlocal active_calls, max_concurrent + del query, browser_policy, policy_audit + with lock: + active_calls += 1 + max_concurrent = max(max_concurrent, active_calls) + time.sleep(0.01) + with lock: + active_calls -= 1 + return { + "ok": True, + "provider": provider, + "results": [{"href": f"https://{provider}.example.com/result"}], + "verification": {"consistent": True}, + } + + request = { + "queries": ["alpha"], + "repeat": 2, + "parallel": 4, + "providers": ["chatgpt_web", "grok_web"], + "verify": {"providers": ["chatgpt_web"], "repeat": 1}, + "browser_policy": {"profile_mode": "allow_profile"}, + } + result = sched._run_search_pipeline( + run_id, + DummyToolRunner(), + request, + {"role": "SEARCHER", "agent_id": "agent-1"}, + ) + assert result["ok"] is True + assert max_concurrent == 1 + + def test_orchestrator_replay_error_paths(tmp_path: Path, monkeypatch) -> None: repo_root = tmp_path / "repo" repo_root.mkdir() diff --git a/apps/orchestrator/tests/test_search_web_strict.py b/apps/orchestrator/tests/test_search_web_strict.py index e16458d..f3e386b 100644 --- a/apps/orchestrator/tests/test_search_web_strict.py +++ b/apps/orchestrator/tests/test_search_web_strict.py @@ -3,6 +3,7 @@ from urllib.parse import urlparse from tooling.search.search_engine import ( + _activate_chat_input, _browser_search, _pick_chat_input_locator, _url_allowed, @@ -71,7 +72,7 @@ def content(self) -> str: assert Path(artifacts["html"]).exists() -def test_pick_chat_input_locator_prefers_standard_inputs() -> None: +def test_pick_chat_input_locator_prefers_provider_specific_editors_before_generic_inputs() -> None: class DummyLocator: def __init__(self, name: str, count: int) -> None: self.name = name @@ -84,12 +85,14 @@ def count(self) -> int: class DummyPage: def __init__(self) -> None: self.locators = { - "textarea": DummyLocator("textarea", 1), - "input[type='text']": DummyLocator("input", 1), + "[data-placeholder='Ask anything'][contenteditable='true']": DummyLocator("grok-placeholder", 1), + ".tiptap.ProseMirror[contenteditable='true']": DummyLocator("grok-tiptap", 1), + "[aria-label='Enter a prompt for Gemini'][contenteditable='true']": DummyLocator("aria", 1), "[role='textbox'][contenteditable='true']": DummyLocator("textbox", 1), "[contenteditable='true'][role='textbox']": DummyLocator("textbox-reversed", 1), - "[aria-label='Enter a prompt for Gemini'][contenteditable='true']": DummyLocator("aria", 1), ".ql-editor[contenteditable='true']": DummyLocator("ql-editor", 1), + "textarea": DummyLocator("textarea", 1), + "input[type='text']": DummyLocator("input", 1), } def locator(self, selector: str) -> DummyLocator: @@ -97,7 +100,7 @@ def locator(self, selector: str) -> DummyLocator: locator = _pick_chat_input_locator(DummyPage()) assert locator is not None - assert locator.name == "textarea" + assert locator.name == "grok-placeholder" def test_pick_chat_input_locator_falls_back_to_contenteditable_textbox() -> None: @@ -113,12 +116,14 @@ def count(self) -> int: class DummyPage: def __init__(self) -> None: self.locators = { - "textarea": DummyLocator("textarea", 0), - "input[type='text']": DummyLocator("input", 0), + "[data-placeholder='Ask anything'][contenteditable='true']": DummyLocator("grok-placeholder", 0), + ".tiptap.ProseMirror[contenteditable='true']": DummyLocator("grok-tiptap", 0), + "[aria-label='Enter a prompt for Gemini'][contenteditable='true']": DummyLocator("aria", 0), "[role='textbox'][contenteditable='true']": DummyLocator("textbox", 1), "[contenteditable='true'][role='textbox']": DummyLocator("textbox-reversed", 0), - "[aria-label='Enter a prompt for Gemini'][contenteditable='true']": DummyLocator("aria", 0), ".ql-editor[contenteditable='true']": DummyLocator("ql-editor", 0), + "textarea": DummyLocator("textarea", 0), + "input[type='text']": DummyLocator("input", 0), } def locator(self, selector: str) -> DummyLocator: @@ -129,6 +134,37 @@ def locator(self, selector: str) -> DummyLocator: assert locator.name == "textbox" +def test_pick_chat_input_locator_supports_grok_tiptap_editor() -> None: + class DummyLocator: + def __init__(self, name: str, count: int) -> None: + self.name = name + self._count = count + self.first = self + + def count(self) -> int: + return self._count + + class DummyPage: + def __init__(self) -> None: + self.locators = { + "[data-placeholder='Ask anything'][contenteditable='true']": DummyLocator("grok-placeholder", 1), + ".tiptap.ProseMirror[contenteditable='true']": DummyLocator("grok-tiptap", 1), + "[aria-label='Enter a prompt for Gemini'][contenteditable='true']": DummyLocator("aria", 0), + "[role='textbox'][contenteditable='true']": DummyLocator("textbox", 0), + "[contenteditable='true'][role='textbox']": DummyLocator("textbox-reversed", 0), + ".ql-editor[contenteditable='true']": DummyLocator("ql-editor", 0), + "textarea": DummyLocator("textarea", 0), + "input[type='text']": DummyLocator("input", 0), + } + + def locator(self, selector: str) -> DummyLocator: + return self.locators[selector] + + locator = _pick_chat_input_locator(DummyPage()) + assert locator is not None + assert locator.name == "grok-placeholder" + + def test_pick_chat_input_locator_returns_none_when_no_supported_input_exists() -> None: class DummyLocator: def __init__(self) -> None: @@ -144,6 +180,97 @@ def locator(self, selector: str) -> DummyLocator: # noqa: ARG002 assert _pick_chat_input_locator(DummyPage()) is None +def test_pick_chat_input_locator_skips_hidden_textarea_for_visible_grok_editor() -> None: + class DummyCandidate: + def __init__(self, name: str, visible: bool) -> None: + self.name = name + self._visible = visible + + def is_visible(self) -> bool: + return self._visible + + class DummyLocator: + def __init__(self, candidates: list[DummyCandidate]) -> None: + self._candidates = candidates + self.first = candidates[0] if candidates else None + + def count(self) -> int: + return len(self._candidates) + + def nth(self, index: int) -> DummyCandidate: + return self._candidates[index] + + class DummyPage: + def __init__(self) -> None: + self.locators = { + "[data-placeholder='Ask anything'][contenteditable='true']": DummyLocator( + [DummyCandidate("grok-visible", True)] + ), + ".tiptap.ProseMirror[contenteditable='true']": DummyLocator([]), + "[aria-label='Enter a prompt for Gemini'][contenteditable='true']": DummyLocator([]), + "[role='textbox'][contenteditable='true']": DummyLocator([]), + "[contenteditable='true'][role='textbox']": DummyLocator([]), + ".ql-editor[contenteditable='true']": DummyLocator([]), + "textarea": DummyLocator([DummyCandidate("hidden-textarea", False)]), + "input[type='text']": DummyLocator([]), + } + + def locator(self, selector: str) -> DummyLocator: + return self.locators[selector] + + locator = _pick_chat_input_locator(DummyPage()) + assert locator is not None + assert locator.name == "grok-visible" + + +def test_activate_chat_input_prefers_normal_click() -> None: + events: list[str] = [] + + class DummyLocator: + def click(self, *, timeout=None, force=False): # noqa: ANN001 + events.append(f"click:{timeout}:{force}") + + def focus(self) -> None: + events.append("focus") + + _activate_chat_input(DummyLocator()) + + assert events == ["click:5000:False"] + + +def test_activate_chat_input_falls_back_to_force_click() -> None: + events: list[str] = [] + + class DummyLocator: + def click(self, *, timeout=None, force=False): # noqa: ANN001 + events.append(f"click:{timeout}:{force}") + if not force: + raise RuntimeError("overlay intercept") + + def focus(self) -> None: + events.append("focus") + + _activate_chat_input(DummyLocator()) + + assert events == ["click:5000:False", "click:5000:True"] + + +def test_activate_chat_input_falls_back_to_focus_when_clicks_fail() -> None: + events: list[str] = [] + + class DummyLocator: + def click(self, *, timeout=None, force=False): # noqa: ANN001 + events.append(f"click:{timeout}:{force}") + raise RuntimeError("still blocked") + + def focus(self) -> None: + events.append("focus") + + _activate_chat_input(DummyLocator()) + + assert events == ["click:5000:False", "click:5000:True", "focus"] + + def test_browser_search_closes_session_before_playwright_exit(monkeypatch, tmp_path: Path) -> None: order: list[str] = [] diff --git a/apps/orchestrator/tests/test_storefront_proof_assets_gate.py b/apps/orchestrator/tests/test_storefront_proof_assets_gate.py new file mode 100644 index 0000000..280c03c --- /dev/null +++ b/apps/orchestrator/tests/test_storefront_proof_assets_gate.py @@ -0,0 +1,224 @@ +from __future__ import annotations + +import importlib.util +import json +import sys +from pathlib import Path + + +def _load_generator_module() -> object: + script_path = Path(__file__).resolve().parents[3] / "scripts" / "generate_storefront_proof_pack_index.py" + spec = importlib.util.spec_from_file_location("cortexpilot_generate_storefront_proof_pack_index", script_path) + module = importlib.util.module_from_spec(spec) + assert spec and spec.loader + spec.loader.exec_module(module) + return module + + +def _load_gate_module() -> object: + script_path = Path(__file__).resolve().parents[3] / "scripts" / "check_storefront_proof_assets.py" + spec = importlib.util.spec_from_file_location("cortexpilot_storefront_proof_assets_gate", script_path) + module = importlib.util.module_from_spec(spec) + assert spec and spec.loader + spec.loader.exec_module(module) + return module + + +def _write_fixture(root: Path) -> None: + (root / "docs" / "assets" / "storefront").mkdir(parents=True, exist_ok=True) + (root / "docs" / "releases" / "assets").mkdir(parents=True, exist_ok=True) + (root / "docs" / "runbooks").mkdir(parents=True, exist_ok=True) + (root / "docs" / "use-cases").mkdir(parents=True, exist_ok=True) + (root / "configs").mkdir(parents=True, exist_ok=True) + + for rel in [ + "docs/releases/assets/news-digest-healthy-proof-2026-03-27.md", + "docs/releases/assets/news-digest-benchmark-summary-2026-03-27.md", + "docs/releases/assets/news-digest-workflow-case-recap-2026-03-27.md", + ]: + path = root / rel + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text("ok\n", encoding="utf-8") + + (root / "docs" / "releases" / "assets" / "news-digest-healthy-proof-summary-2026-03-27.json").write_text( + json.dumps({"artifact_type": "healthy-proof-summary"}, ensure_ascii=False, indent=2) + "\n", + encoding="utf-8", + ) + (root / "docs" / "releases" / "assets" / "news-digest-benchmark-summary-2026-03-27.json").write_text( + json.dumps({"artifact_type": "benchmark-summary"}, ensure_ascii=False, indent=2) + "\n", + encoding="utf-8", + ) + (root / "docs" / "releases" / "assets" / "news-digest-healthy-proof-gemini-2026-03-27.png").write_text( + "png\n", + encoding="utf-8", + ) + (root / "docs" / "assets" / "storefront" / "dashboard-home-live-1440x900.png").write_text("png\n", encoding="utf-8") + (root / "docs" / "assets" / "storefront" / "dashboard-command-tower-live-1440x900.png").write_text("png\n", encoding="utf-8") + (root / "docs" / "assets" / "storefront" / "dashboard-runs-live-1440x900.png").write_text("png\n", encoding="utf-8") + (root / "docs" / "assets" / "storefront" / "dashboard-live-healthy-loop.gif").write_text("gif\n", encoding="utf-8") + (root / "docs" / "releases" / "assets" / "news-digest-proof-pack-2026-03-27.json").write_text( + json.dumps( + { + "artifact_type": "news_digest_public_proof_pack", + "primary_assets": { + "proof_summary_markdown": "docs/releases/assets/news-digest-healthy-proof-2026-03-27.md", + "proof_summary_json": "docs/releases/assets/news-digest-healthy-proof-summary-2026-03-27.json", + "benchmark_summary_markdown": "docs/releases/assets/news-digest-benchmark-summary-2026-03-27.md", + "benchmark_summary_json": "docs/releases/assets/news-digest-benchmark-summary-2026-03-27.json", + "workflow_case_recap_markdown": "docs/releases/assets/news-digest-workflow-case-recap-2026-03-27.md", + "demo_status_markdown": "docs/assets/storefront/demo-status.md" + }, + "supporting_assets": { + "gemini_proof_screenshot": "docs/releases/assets/news-digest-healthy-proof-gemini-2026-03-27.png", + "dashboard_home_capture": "docs/assets/storefront/dashboard-home-live-1440x900.png", + "dashboard_command_tower_capture": "docs/assets/storefront/dashboard-command-tower-live-1440x900.png", + "dashboard_runs_capture": "docs/assets/storefront/dashboard-runs-live-1440x900.png", + "healthy_live_capture_gif": "docs/assets/storefront/dashboard-live-healthy-loop.gif" + } + }, + ensure_ascii=False, + indent=2, + ) + + "\n", + encoding="utf-8", + ) + + (root / "docs" / "use-cases" / "index.html").write_text( + """ +

First proven workflow and public proof pack

+ Open proof-pack index +

The current recap story now has one tracked news_digest Workflow Case asset, tracked healthy local captures and proof assets, and one remaining broader benchmark gap.

+

The current benchmark story is a tracked single-run baseline, not a broad release average.

+

Global proof-pack index across public proven and showcase bundles

+ """, + encoding="utf-8", + ) + (root / "docs" / "assets" / "storefront" / "demo-status.md").write_text( + """ + | Proof class | Current status | Notes | + | --- | --- | --- | + | Healthy backend-backed dashboard capture set | present | tracked English-first home, Command Tower session, and Runs captures from a clean local runtime root | + | Healthy backend-backed live GIF | present | tracked multi-page walkthrough of the official first public happy path | + ## Truth Boundary + - these tracked captures are safe repo-side proof of a healthy local first public path, not proof of hosted production scale or live GitHub publication state. + """, + encoding="utf-8", + ) + (root / "docs" / "assets" / "storefront" / "live-capture-requirements.json").write_text( + json.dumps( + { + "artifact_type": "cortexpilot_storefront_live_capture_requirements", + "required_assets": [ + {"asset_id": "healthy_live_capture_gif", "status": "present"}, + {"asset_id": "healthy_english_first_dashboard_home_capture", "status": "present"}, + {"asset_id": "healthy_english_first_command_tower_capture", "status": "present"}, + {"asset_id": "healthy_english_first_runs_capture", "status": "present"}, + ], + }, + ensure_ascii=False, + indent=2, + ) + + "\n", + encoding="utf-8", + ) + (root / "docs" / "runbooks" / "storefront-share-kit.md").write_text( + """ + ## Proof Status By Asset Type + - Healthy backend-backed dashboard capture set + - Healthy backend-backed live GIF + ## Safe Post Angles + - safe to reference as repo-tracked proof, not as proof of live GitHub publication + """, + encoding="utf-8", + ) + + (root / "configs" / "storefront_proof_bundle_registry.json").write_text( + json.dumps( + { + "schema_version": 1, + "artifact_type": "cortexpilot_storefront_proof_bundle_registry", + "vocabulary_contract": { + "proven_workflow_label": "first proven workflow", + "proof_pack_label": "public proof pack", + "showcase_label": "showcase expansion", + }, + "bundles": [ + { + "bundle_id": "news_digest", + "task_template": "news_digest", + "proof_state": "release_proven", + "claim_scope": "official_first_public_baseline", + "authority_level": "repo_side_public_proof", + "public_entrypoint": "docs/use-cases/index.html", + "pack_manifest": "docs/releases/assets/news-digest-proof-pack-2026-03-27.json", + "capture_contract": { + "healthy_live_capture_gif_present": True, + "healthy_english_first_public_capture_set_present": True, + "current_tracked_dashboard_captures": "healthy_english_first_backend_backed_local", + }, + "missing_expected_artifacts": [ + "broader_multi_round_benchmark", + ], + }, + { + "bundle_id": "topic_brief", + "proof_state": "showcase_only", + "missing_expected_artifacts": ["dedicated_healthy_proof_summary"], + }, + { + "bundle_id": "page_brief", + "proof_state": "showcase_only", + "missing_expected_artifacts": ["dedicated_healthy_proof_summary"], + }, + ], + }, + ensure_ascii=False, + indent=2, + ) + + "\n", + encoding="utf-8", + ) + + generator = _load_generator_module() + generator.ROOT = root + generator.REGISTRY_PATH = root / "configs" / "storefront_proof_bundle_registry.json" + generator.OUTPUT_PATH = root / "docs" / "assets" / "storefront" / "proof-pack-index.json" + rendered = generator.build_index(generator._load_json(generator.REGISTRY_PATH)) + generator.OUTPUT_PATH.write_text(json.dumps(rendered, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + + +def test_storefront_proof_assets_gate_passes_with_expected_index(tmp_path: Path, monkeypatch) -> None: + module = _load_gate_module() + _write_fixture(tmp_path) + module.ROOT = tmp_path + module.PROOF_PACK_INDEX = tmp_path / "docs" / "assets" / "storefront" / "proof-pack-index.json" + module.DEMO_STATUS_PATH = tmp_path / "docs" / "assets" / "storefront" / "demo-status.md" + module.LIVE_CAPTURE_REQUIREMENTS_PATH = tmp_path / "docs" / "assets" / "storefront" / "live-capture-requirements.json" + module.SHARE_KIT_PATH = tmp_path / "docs" / "runbooks" / "storefront-share-kit.md" + module.USE_CASES_PATH = tmp_path / "docs" / "use-cases" / "index.html" + monkeypatch.setattr(sys, "argv", ["check_storefront_proof_assets.py"]) + assert module.main() == 0 + + +def test_storefront_proof_assets_gate_fails_when_news_digest_loses_release_proven( + tmp_path: Path, capsys, monkeypatch +) -> None: + module = _load_gate_module() + _write_fixture(tmp_path) + index_path = tmp_path / "docs" / "assets" / "storefront" / "proof-pack-index.json" + payload = json.loads(index_path.read_text(encoding="utf-8")) + payload["bundles"][0]["proof_state"] = "showcase_only" + index_path.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + + module.ROOT = tmp_path + module.PROOF_PACK_INDEX = index_path + module.DEMO_STATUS_PATH = tmp_path / "docs" / "assets" / "storefront" / "demo-status.md" + module.LIVE_CAPTURE_REQUIREMENTS_PATH = tmp_path / "docs" / "assets" / "storefront" / "live-capture-requirements.json" + module.SHARE_KIT_PATH = tmp_path / "docs" / "runbooks" / "storefront-share-kit.md" + module.USE_CASES_PATH = tmp_path / "docs" / "use-cases" / "index.html" + monkeypatch.setattr(sys, "argv", ["check_storefront_proof_assets.py"]) + + rc = module.main() + out = capsys.readouterr().out + assert rc == 1 + assert "release_proven" in out diff --git a/apps/orchestrator/tests/test_tool_pipeline_integration.py b/apps/orchestrator/tests/test_tool_pipeline_integration.py index b53d4f8..ee6f2dd 100644 --- a/apps/orchestrator/tests/test_tool_pipeline_integration.py +++ b/apps/orchestrator/tests/test_tool_pipeline_integration.py @@ -202,6 +202,7 @@ def test_tool_pipeline_network_gate_denied(tmp_path: Path, monkeypatch) -> None: repo = tmp_path / "repo" repo.mkdir() _init_repo(repo) + (repo / "codex_home").mkdir() (repo / "README.md").write_text("hello", encoding="utf-8") _git(["git", "add", "README.md", "policies/command_allowlist.json"], repo) _git(["git", "commit", "-m", "init"], repo) @@ -330,6 +331,66 @@ def run_script(self, _script: str, _url: str) -> dict: ) +def test_tool_pipeline_searcher_short_circuits_before_codex_runner(tmp_path: Path, monkeypatch) -> None: + _reset_scheduler_tool_hooks(monkeypatch) + repo = tmp_path / "repo" + repo.mkdir() + _init_repo(repo) + (repo / "codex_home").mkdir() + (repo / "README.md").write_text("hello", encoding="utf-8") + _git(["git", "add", "README.md", "policies/command_allowlist.json"], repo) + _git(["git", "commit", "-m", "init"], repo) + + runtime_root = tmp_path / "runtime" + runs_root = runtime_root / "runs" + worktree_root = runtime_root / "worktrees" + _configure_runtime_roots(monkeypatch, runtime_root, runs_root, worktree_root) + monkeypatch.setenv("CORTEXPILOT_SEARCH_MODE", "mock") + monkeypatch.setenv("CORTEXPILOT_CHAIN_EXEC_MODE", "inline") + monkeypatch.setenv("CORTEXPILOT_RUNNER", "codex") + monkeypatch.setenv("CORTEXPILOT_MCP_ONLY", "1") + monkeypatch.setenv("CORTEXPILOT_ALLOW_CODEX_EXEC", "1") + + search_path = repo / "search_requests.json" + search_body = _write_json( + search_path, + {"queries": ["cortexpilot"], "providers": ["chatgpt_web", "grok_web"], "repeat": 2, "parallel": 2}, + ) + _pin_tool_requests( + monkeypatch, + search_request={"queries": ["cortexpilot"], "providers": ["chatgpt_web", "grok_web"], "repeat": 2, "parallel": 2}, + ) + + artifacts = [ + { + "name": "search_requests.json", + "uri": search_path.name, + "sha256": _sha256_text(search_body), + } + ] + + contract = _base_contract("task_search_short_circuit", artifacts) + contract["assigned_agent"]["role"] = "SEARCHER" + contract_path = repo / "contract.json" + _write_json(contract_path, contract) + + monkeypatch.setattr( + "cortexpilot_orch.runners.codex_runner.CodexRunner.run_contract", + lambda *_args, **_kwargs: (_ for _ in ()).throw(AssertionError("codex runner should not execute after search pipeline success")), + ) + + monkeypatch.chdir(repo) + orch = Orchestrator(repo) + run_id = orch.execute_task(contract_path, mock_mode=False) + + manifest = json.loads((runs_root / run_id / "manifest.json").read_text(encoding="utf-8")) + run_dir = runs_root / run_id + assert manifest["status"] == "SUCCESS" + assert (run_dir / "artifacts" / "search_results.json").exists() + assert (run_dir / "reports" / "evidence_bundle.json").exists() + assert (run_dir / "reports" / "task_result.json").exists() + + def test_tool_pipeline_browser_failure_marks_run_failed(tmp_path: Path, monkeypatch) -> None: _reset_scheduler_tool_hooks(monkeypatch) repo = tmp_path / "repo" diff --git a/configs/docs_render_manifest.json b/configs/docs_render_manifest.json index 216a2e4..2e70527 100644 --- a/configs/docs_render_manifest.json +++ b/configs/docs_render_manifest.json @@ -2,6 +2,21 @@ "schema_version": 1, "description": "Minimal generated-doc outputs kept after the public-surface reduction.", "entries": [ + { + "output_path": "docs/assets/storefront/proof-pack-index.json", + "mode": "full_render", + "source_inputs": [ + "scripts/generate_storefront_proof_pack_index.py", + "configs/storefront_proof_bundle_registry.json", + "docs/releases/assets/news-digest-proof-pack-2026-03-27.json" + ], + "generator": "python3 scripts/generate_storefront_proof_pack_index.py", + "freshness_strategy": "timestamp", + "authoritative_for": [ + "public_storefront_proof_pack_index" + ], + "human_editable_regions": "none" + }, { "output_path": "docs/governance/ui-button-coverage-matrix.md", "mode": "full_render", @@ -25,7 +40,7 @@ "configs/ci_governance_policy.json" ], "generator": "python3 scripts/render_docs.py", - "freshness_strategy": "timestamp", + "freshness_strategy": "existence_only", "authoritative_for": [ "generated_ci_topology_summary" ], @@ -38,7 +53,7 @@ "scripts/render_docs.py" ], "generator": "python3 scripts/render_docs.py", - "freshness_strategy": "timestamp", + "freshness_strategy": "existence_only", "authoritative_for": [ "generated_current_run_summary" ], @@ -51,7 +66,7 @@ "scripts/render_docs.py" ], "generator": "python3 scripts/render_docs.py", - "freshness_strategy": "timestamp", + "freshness_strategy": "existence_only", "authoritative_for": [ "generated_coverage_summary" ], diff --git a/configs/env.registry.json b/configs/env.registry.json index a0b6212..efff430 100644 --- a/configs/env.registry.json +++ b/configs/env.registry.json @@ -6100,9 +6100,9 @@ "scope": "platform", "secret": false, "required": false, - "default": "1", + "default": "0", "owner": "platform", - "description": "Boolean switch controlling mandatory local verification bundle in pre-push gate.", + "description": "Controls the pre-push local verification profile. `0` keeps the default fast bundle, `1` enables the old strict local mirror, and `off` requires break-glass to skip local verification entirely.", "consumers": [ "scripts/pre_push_quality_gate.sh" ] diff --git a/configs/github_control_plane_policy.json b/configs/github_control_plane_policy.json index 0da3778..586c460 100644 --- a/configs/github_control_plane_policy.json +++ b/configs/github_control_plane_policy.json @@ -26,6 +26,22 @@ "required": true, "mode": "api" }, + "secret_scanning": { + "required": true, + "mode": "api" + }, + "secret_scanning_push_protection": { + "required": true, + "mode": "api" + }, + "secret_scanning_non_provider_patterns": { + "required": true, + "mode": "api" + }, + "secret_scanning_validity_checks": { + "required": true, + "mode": "api" + }, "dependabot_config": { "required": true, "mode": "repo_file", diff --git a/configs/storefront_proof_bundle_registry.json b/configs/storefront_proof_bundle_registry.json new file mode 100644 index 0000000..4c588fc --- /dev/null +++ b/configs/storefront_proof_bundle_registry.json @@ -0,0 +1,77 @@ +{ + "schema_version": 1, + "artifact_type": "cortexpilot_storefront_proof_bundle_registry", + "vocabulary_contract": { + "proven_workflow_label": "first proven workflow", + "proof_pack_label": "public proof pack", + "showcase_label": "showcase expansion" + }, + "bundles": [ + { + "bundle_id": "news_digest", + "task_template": "news_digest", + "proof_state": "release_proven", + "claim_scope": "official_first_public_baseline", + "authority_level": "repo_side_public_proof", + "public_entrypoint": "docs/use-cases/index.html", + "pack_manifest": "docs/releases/assets/news-digest-proof-pack-2026-03-27.json", + "safe_public_claims": [ + "news_digest is the only official release-proven first public baseline today", + "the public trust bundle includes proof summary, benchmark summary, Workflow Case recap, and demo-status ledger", + "the benchmark remains a single-run baseline" + ], + "forbidden_claims": [ + "topic_brief is equally release-proven today", + "page_brief is equally release-proven today", + "the current benchmark is a broad multi-run release average", + "current storefront captures prove a fully healthy end-to-end public session" + ], + "capture_contract": { + "healthy_live_capture_gif_present": true, + "healthy_english_first_public_capture_set_present": true, + "current_tracked_dashboard_captures": "healthy_english_first_backend_backed_local" + }, + "missing_expected_artifacts": [ + "broader_multi_round_benchmark" + ] + }, + { + "bundle_id": "topic_brief", + "task_template": "topic_brief", + "proof_state": "showcase_only", + "claim_scope": "public_showcase_path", + "authority_level": "repo_side_story_surface", + "public_entrypoint": "docs/use-cases/index.html", + "safe_public_claims": [ + "topic_brief is a public showcase path with search-backed evidence" + ], + "forbidden_claims": [ + "topic_brief is an equally release-proven baseline today" + ], + "missing_expected_artifacts": [ + "dedicated_healthy_proof_summary", + "dedicated_benchmark_summary", + "share_ready_recap" + ] + }, + { + "bundle_id": "page_brief", + "task_template": "page_brief", + "proof_state": "showcase_only", + "claim_scope": "public_showcase_path", + "authority_level": "repo_side_story_surface", + "public_entrypoint": "docs/use-cases/index.html", + "safe_public_claims": [ + "page_brief is a browser-backed public showcase path" + ], + "forbidden_claims": [ + "page_brief is an equally release-proven baseline today" + ], + "missing_expected_artifacts": [ + "dedicated_healthy_proof_summary", + "dedicated_benchmark_summary", + "share_ready_recap" + ] + } + ] +} diff --git a/docs/README.md b/docs/README.md index 54dd860..87c012f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -15,29 +15,19 @@ summary for contributors and maintainers. `configs/docs_nav_registry.json` is the machine source of truth for the active docs inventory. This file is the human-readable summary of that registry. -Public-fixture hygiene stays part of the docs contract too: orchestrator probe -and security fixtures must use synthetic token fragments plus generic workspace -sample roots instead of maintainer-local paths or raw token-looking literals. -The same contract now keeps `security_scan.sh` placeholder URIs pinned to the -exact `example.com` host and preserves `.jsonl` hints in portable temp-report -names, while `scripts/check_public_sensitive_surface.py` blocks tracked -local-path/PII/raw-token/runtime-file leaks on current public source surfaces. -The same gate family now includes `scripts/check_github_security_alerts.py`, -which fails closed on open GitHub secret-scanning and code-scanning alerts in -repo hygiene, host-compatible pre-commit quality, a dedicated pre-commit hook, -pre-push, and Quick Feedback. -Workflow governance now has matching repo-owned entrypoints too: -`scripts/check_workflow_static_security.sh` pairs `actionlint` with -`zizmor` in one fail-closed gate, `scripts/check_trivy_repo_scan.sh` -owns the repo-wide Trivy filesystem/dependency lane, and -`scripts/check_secret_scan_closeout.sh` is the closeout wrapper that can rerun -secret scanning against the current tree or a fresh clone. Pull requests also -run the official GitHub Dependency Review action under the repo-owned -`.github/dependency-review-config.yml` policy file. On GitHub-hosted -`trusted_pr`, `untrusted_pr`, and hosted-first `push_main` routes the live -alerts query stays advisory for both Quick Feedback and the hosted policy -slice because the integration token may not be allowed to read the alerts APIs -there and a fresh hosted `push_main` route may not have live analysis yet. +Daily local verification lives in the root [README](../README.md). Treat this +file as the docs inventory map, not as a second CI manual. + +For CI/security/documentation truth, prefer the machine-owned surfaces and +repo-owned gates instead of restating the same rules here: + +- `configs/docs_nav_registry.json` +- `configs/ci_governance_policy.json` +- `configs/github_control_plane_policy.json` +- `scripts/check_public_sensitive_surface.py` +- `scripts/check_workflow_static_security.sh` +- `scripts/check_trivy_repo_scan.sh` +- `scripts/check_secret_scan_closeout.sh` ## Repository Entry @@ -97,6 +87,8 @@ navigation set. - `docs/runbooks/github-storefront-manual-steps.md`: exact GitHub UI values and manual storefront steps - `docs/releases/first-public-release-draft.md`: repo-side draft source for the first public GitHub Release - `docs/assets/storefront/demo-status.md`: status ledger for tracked public demo and proof assets +- `docs/assets/storefront/proof-pack-index.json`: machine-readable public proof bundle index for proven and showcase storefront slices +- `docs/assets/storefront/live-capture-requirements.json`: machine-readable contract for the remaining healthy GIF and English-first public capture deliverables - `docs/assets/storefront/benchmark-methodology.md`: public benchmark evidence contract and wording boundary - `docs/architecture/ecosystem-and-builder-surfaces-v1.md`: ecosystem binding, first-run distribution loop, and current builder/client entry points - `docs/runbooks/render-hosted-operator-pilot.md`: repo-side Git-backed hosted operator blueprint for future guarded Render pilots; use it to stage env/health/rollback/support/security requirements without implying a live hosted service diff --git a/docs/assets/storefront/README.md b/docs/assets/storefront/README.md index 42c7de2..01e8a54 100644 --- a/docs/assets/storefront/README.md +++ b/docs/assets/storefront/README.md @@ -9,14 +9,17 @@ and social-preview use. - `first-loop-storyboard.svg`: shareable storyboard for the PM -> Command Tower -> Runs story - `first-loop-storyboard.png`: exportable static storyboard preview - `first-loop-storyboard.gif`: tracked storyboard animation (not a live product capture) -- `dashboard-home-live-1440x900.png`: tracked real screenshot from the dashboard home page in local degraded mode -- `dashboard-command-tower-live-1440x900.png`: tracked real Command Tower screenshot in local degraded mode -- `dashboard-runs-live-1440x900.png`: tracked real Runs screenshot in local degraded mode -- `dashboard-live-degraded-loop.gif`: tracked real dashboard GIF in local degraded mode +- `dashboard-home-live-1440x900.png`: tracked healthy English-first dashboard home screenshot +- `dashboard-command-tower-live-1440x900.png`: tracked healthy English-first Command Tower session screenshot +- `dashboard-runs-live-1440x900.png`: tracked healthy English-first Runs / Proof & Replay screenshot +- `dashboard-live-healthy-loop.gif`: tracked healthy backend-backed dashboard GIF +- `dashboard-live-degraded-loop.gif`: historical degraded dashboard GIF retained as a truth-boundary reference - `desktop-shell-live-1440x900.png`: tracked real screenshot from the desktop snapshot pipeline - `social-preview-source.svg`: source artwork for a GitHub social preview export - `social-preview-1280x640.png`: exported GitHub social preview candidate - `demo-status.md`: explicit status ledger for real demo/benchmark asset closure +- `proof-pack-index.json`: machine-readable public proof bundle index +- `live-capture-requirements.json`: machine-readable contract for the remaining healthy public capture deliverables ## Rules @@ -27,4 +30,7 @@ and social-preview use. - treat `first-loop-storyboard.gif` as a storyboard animation, not as a real live-product recording - label degraded local screenshots honestly when backend data is unavailable - label degraded local GIF captures honestly when backend data is unavailable +- label healthy local dashboard captures as repo-side local proof, not as proof of hosted production scale or live GitHub publication +- treat `proof-pack-index.json` as the public proof bundle SSOT for repo-tracked proof surfaces +- treat `live-capture-requirements.json` as the SSOT for the remaining healthy GIF / English-first capture deliverables - regenerate tracked exports on macOS with `bash scripts/export_storefront_assets.sh` (supports `inkscape` or `rsvg-convert` as optional SVG fallbacks) diff --git a/docs/assets/storefront/dashboard-command-tower-live-1440x900.png b/docs/assets/storefront/dashboard-command-tower-live-1440x900.png index f567bf4..a815245 100644 Binary files a/docs/assets/storefront/dashboard-command-tower-live-1440x900.png and b/docs/assets/storefront/dashboard-command-tower-live-1440x900.png differ diff --git a/docs/assets/storefront/dashboard-home-live-1440x900.png b/docs/assets/storefront/dashboard-home-live-1440x900.png index 04b161f..336ef04 100644 Binary files a/docs/assets/storefront/dashboard-home-live-1440x900.png and b/docs/assets/storefront/dashboard-home-live-1440x900.png differ diff --git a/docs/assets/storefront/dashboard-live-healthy-loop.gif b/docs/assets/storefront/dashboard-live-healthy-loop.gif new file mode 100644 index 0000000..72fa676 Binary files /dev/null and b/docs/assets/storefront/dashboard-live-healthy-loop.gif differ diff --git a/docs/assets/storefront/dashboard-runs-live-1440x900.png b/docs/assets/storefront/dashboard-runs-live-1440x900.png index 558419a..e33fcb9 100644 Binary files a/docs/assets/storefront/dashboard-runs-live-1440x900.png and b/docs/assets/storefront/dashboard-runs-live-1440x900.png differ diff --git a/docs/assets/storefront/demo-status.md b/docs/assets/storefront/demo-status.md index 1a374d6..3466d2d 100644 --- a/docs/assets/storefront/demo-status.md +++ b/docs/assets/storefront/demo-status.md @@ -9,10 +9,10 @@ ones that still need a real capture pass. - `first-loop-storyboard.svg`: shareable storyboard of the PM -> Command Tower -> Runs loop - `first-loop-storyboard.png`: static storyboard export - `first-loop-storyboard.gif`: storyboard animation export -- `dashboard-home-live-1440x900.png`: real dashboard home screenshot captured from a local production server -- `dashboard-command-tower-live-1440x900.png`: real Command Tower screenshot captured from a local production server -- `dashboard-runs-live-1440x900.png`: real Runs page screenshot captured from a local production server -- `dashboard-live-degraded-loop.gif`: real multi-page dashboard GIF captured from a local degraded run +- `dashboard-home-live-1440x900.png`: healthy English-first dashboard home screenshot captured from a clean local runtime root +- `dashboard-command-tower-live-1440x900.png`: healthy English-first Command Tower session screenshot captured from the same verified path +- `dashboard-runs-live-1440x900.png`: healthy English-first Runs / Proof & Replay screenshot captured from the same verified path +- `dashboard-live-healthy-loop.gif`: healthy backend-backed multi-page dashboard GIF captured from the same verified path - `desktop-shell-live-1440x900.png`: real desktop preview screenshot captured from the app snapshot pipeline - `social-preview-source.svg`: editable social card source - `social-preview-1280x640.png`: upload-ready social card candidate @@ -20,6 +20,7 @@ ones that still need a real capture pass. - `docs/releases/assets/news-digest-healthy-proof-gemini-2026-03-27.png`: successful Gemini proof screenshot - `docs/releases/assets/news-digest-healthy-proof-grok-2026-03-27.png`: successful Grok proof screenshot - `docs/releases/assets/news-digest-benchmark-summary-2026-03-27.md`: first tracked public `news_digest` baseline summary +- `docs/releases/assets/news-digest-proof-pack-2026-03-27.json`: machine-readable proof-pack directory for the current public trust bundle - `docs/releases/assets/news-digest-workflow-case-recap-2026-03-27.md`: share-ready Workflow Case recap for the official first public baseline ## Current Proof Ledger @@ -27,7 +28,8 @@ ones that still need a real capture pass. | Proof class | Current status | Notes | | --- | --- | --- | | Storyboard explainer assets | present | useful for explaining the loop, not proving runtime health | -| Real local degraded dashboard captures | present | honest supporting evidence, but not healthy backend-backed proof | +| Healthy backend-backed dashboard capture set | present | tracked English-first home, Command Tower session, and Runs captures from a clean local runtime root | +| Healthy backend-backed live GIF | present | tracked multi-page walkthrough of the official first public happy path | | Desktop preview capture | present | shows the shell surface only | | Healthy backend-backed `news_digest` public proof set | present | tracked proof summary: `docs/releases/assets/news-digest-healthy-proof-2026-03-27.md` | | Public benchmark artifact from a real tracked run | present | first tracked single-run baseline: `docs/releases/assets/news-digest-benchmark-summary-2026-03-27.md` | @@ -36,7 +38,6 @@ ones that still need a real capture pass. ## Still Missing -- a tracked healthy live-capture GIF for the official first public happy path - a broader multi-round public benchmark artifact beyond the current single-run baseline summary - a live GitHub social preview upload @@ -47,9 +48,17 @@ It prevents “we already have assets” from drifting into fake maturity. A sou file, a storyboard, a storyboard animation, and a production-ready live capture are different things. +The public storytelling shorthand is now: + +- **first proven workflow** = `news_digest` +- **public proof pack** = healthy proof summary + benchmark summary + Workflow Case recap + demo-status ledger +- **showcase paths** = `topic_brief` and `page_brief` until they have their own healthy proof bundles + ## Truth Boundary -- `dashboard-home-live-1440x900.png`, `dashboard-command-tower-live-1440x900.png`, `dashboard-runs-live-1440x900.png`, and `dashboard-live-degraded-loop.gif` are real captures from the dashboard server, but they were captured in a local degraded state where backend data was unavailable. +- `dashboard-home-live-1440x900.png`, `dashboard-command-tower-live-1440x900.png`, `dashboard-runs-live-1440x900.png`, and `dashboard-live-healthy-loop.gif` are real healthy captures from the dashboard server backed by the clean local runtime root `.runtime-cache/storefront-final-capture-english`. +- these tracked captures are safe repo-side proof of a healthy local first public path, not proof of hosted production scale, stable multi-run release averages, or live GitHub publication state. +- `dashboard-live-degraded-loop.gif` remains a historical degraded capture and should stay labeled as degraded if it is referenced at all. - `desktop-shell-live-1440x900.png` is a real screenshot from the desktop snapshot pipeline. - `social-preview-1280x640.png` is a repo-tracked upload candidate for the GitHub social preview setting, not proof that the live GitHub setting has already been applied. - `docs/releases/first-public-release-draft.md` remains the repo-side draft source that fed the published release notes. @@ -64,4 +73,4 @@ are different things. a historical blocker receipt from earlier failed healthy-proof attempts. - `https://xiaojiou176-open.github.io/CortexPilot-public/` is now the live GitHub Pages site backed by `main` / `/docs`. -- None of these captures should be described as proof of a fully healthy end-to-end backend session. +- None of these captures should be described as proof of live hosted readiness, live GitHub publication state, or broad production-scale stability. diff --git a/docs/assets/storefront/live-capture-requirements.json b/docs/assets/storefront/live-capture-requirements.json new file mode 100644 index 0000000..d856e08 --- /dev/null +++ b/docs/assets/storefront/live-capture-requirements.json @@ -0,0 +1,56 @@ +{ + "artifact_type": "cortexpilot_storefront_live_capture_requirements", + "applies_to_bundle": "news_digest", + "summary": "This contract records the healthy public capture deliverables for the official first public baseline and the tracked assets that currently satisfy them.", + "required_assets": [ + { + "asset_id": "healthy_live_capture_gif", + "status": "present", + "required": true, + "description": "A healthy backend-backed GIF that shows the official news_digest first public happy path.", + "path": "docs/assets/storefront/dashboard-live-healthy-loop.gif", + "must_not_be": [ + "storyboard animation", + "local degraded capture", + "mocked or synthetic walkthrough" + ] + }, + { + "asset_id": "healthy_english_first_dashboard_home_capture", + "status": "present", + "required": true, + "description": "A healthy English-first dashboard home screenshot captured from the public first-run path.", + "path": "docs/assets/storefront/dashboard-home-live-1440x900.png", + "must_not_be": [ + "localized non-English default capture", + "degraded data capture" + ] + }, + { + "asset_id": "healthy_english_first_command_tower_capture", + "status": "present", + "required": true, + "description": "A healthy English-first Command Tower screenshot from the same verified path.", + "path": "docs/assets/storefront/dashboard-command-tower-live-1440x900.png", + "must_not_be": [ + "degraded data capture" + ] + }, + { + "asset_id": "healthy_english_first_runs_capture", + "status": "present", + "required": true, + "description": "A healthy English-first Runs / Proof & Replay screenshot from the same verified path.", + "path": "docs/assets/storefront/dashboard-runs-live-1440x900.png", + "must_not_be": [ + "degraded data capture" + ] + } + ], + "success_criteria": [ + "All required assets exist as tracked files or release assets.", + "The captures come from a healthy backend-backed path, not a degraded local fallback.", + "The visible UI language is English-first.", + "The assets can be safely linked from the public first proven workflow story without overstating proof." + ] +} diff --git a/docs/assets/storefront/proof-pack-index.json b/docs/assets/storefront/proof-pack-index.json new file mode 100644 index 0000000..b841a74 --- /dev/null +++ b/docs/assets/storefront/proof-pack-index.json @@ -0,0 +1,166 @@ +{ + "artifact_type": "cortexpilot_public_proof_pack_index", + "generated_by": "scripts/generate_storefront_proof_pack_index.py", + "source_registry": "configs/storefront_proof_bundle_registry.json", + "vocabulary_contract": { + "proven_workflow_label": "first proven workflow", + "proof_pack_label": "public proof pack", + "showcase_label": "showcase expansion" + }, + "bundles": [ + { + "bundle_id": "news_digest", + "task_template": "news_digest", + "proof_state": "release_proven", + "claim_scope": "official_first_public_baseline", + "authority_level": "repo_side_public_proof", + "public_entrypoint": "docs/use-cases/index.html", + "pack_manifest": "docs/releases/assets/news-digest-proof-pack-2026-03-27.json", + "safe_public_claims": [ + "news_digest is the only official release-proven first public baseline today", + "the public trust bundle includes proof summary, benchmark summary, Workflow Case recap, and demo-status ledger", + "the benchmark remains a single-run baseline" + ], + "forbidden_claims": [ + "topic_brief is equally release-proven today", + "page_brief is equally release-proven today", + "the current benchmark is a broad multi-run release average", + "current storefront captures prove a fully healthy end-to-end public session" + ], + "capture_contract": { + "healthy_live_capture_gif_present": true, + "healthy_english_first_public_capture_set_present": true, + "current_tracked_dashboard_captures": "healthy_english_first_backend_backed_local" + }, + "missing_expected_artifacts": [ + "broader_multi_round_benchmark" + ], + "assets": [ + { + "path": "docs/releases/assets/news-digest-healthy-proof-2026-03-27.md", + "role": "healthy_proof_summary", + "format": "markdown", + "truth_class": "repo_side_proof", + "required_for_claim": true + }, + { + "path": "docs/releases/assets/news-digest-healthy-proof-summary-2026-03-27.json", + "role": "healthy_proof_summary_machine", + "format": "json", + "truth_class": "repo_side_machine_summary", + "required_for_claim": true + }, + { + "path": "docs/releases/assets/news-digest-benchmark-summary-2026-03-27.md", + "role": "benchmark_summary", + "format": "markdown", + "truth_class": "repo_side_benchmark", + "required_for_claim": true + }, + { + "path": "docs/releases/assets/news-digest-benchmark-summary-2026-03-27.json", + "role": "benchmark_summary_machine", + "format": "json", + "truth_class": "repo_side_machine_summary", + "required_for_claim": true + }, + { + "path": "docs/releases/assets/news-digest-workflow-case-recap-2026-03-27.md", + "role": "workflow_case_recap", + "format": "markdown", + "truth_class": "share_ready_recap", + "required_for_claim": true + }, + { + "path": "docs/assets/storefront/demo-status.md", + "role": "demo_status_ledger", + "format": "markdown", + "truth_class": "truth_boundary_ledger", + "required_for_claim": true + }, + { + "path": "docs/releases/assets/news-digest-healthy-proof-gemini-2026-03-27.png", + "role": "gemini_proof_screenshot", + "format": "png", + "truth_class": "supporting_capture", + "required_for_claim": false + }, + { + "path": "docs/releases/assets/news-digest-healthy-proof-grok-2026-03-27.png", + "role": "grok_proof_screenshot", + "format": "png", + "truth_class": "supporting_capture", + "required_for_claim": false + }, + { + "path": "docs/assets/storefront/dashboard-home-live-1440x900.png", + "role": "dashboard_home_capture", + "format": "png", + "truth_class": "supporting_capture", + "required_for_claim": false + }, + { + "path": "docs/assets/storefront/dashboard-command-tower-live-1440x900.png", + "role": "dashboard_command_tower_capture", + "format": "png", + "truth_class": "supporting_capture", + "required_for_claim": false + }, + { + "path": "docs/assets/storefront/dashboard-runs-live-1440x900.png", + "role": "dashboard_runs_capture", + "format": "png", + "truth_class": "supporting_capture", + "required_for_claim": false + }, + { + "path": "docs/assets/storefront/dashboard-live-healthy-loop.gif", + "role": "healthy_live_capture_gif", + "format": "gif", + "truth_class": "supporting_capture", + "required_for_claim": false + } + ] + }, + { + "bundle_id": "topic_brief", + "task_template": "topic_brief", + "proof_state": "showcase_only", + "claim_scope": "public_showcase_path", + "authority_level": "repo_side_story_surface", + "public_entrypoint": "docs/use-cases/index.html", + "safe_public_claims": [ + "topic_brief is a public showcase path with search-backed evidence" + ], + "forbidden_claims": [ + "topic_brief is an equally release-proven baseline today" + ], + "missing_expected_artifacts": [ + "dedicated_healthy_proof_summary", + "dedicated_benchmark_summary", + "share_ready_recap" + ], + "assets": [] + }, + { + "bundle_id": "page_brief", + "task_template": "page_brief", + "proof_state": "showcase_only", + "claim_scope": "public_showcase_path", + "authority_level": "repo_side_story_surface", + "public_entrypoint": "docs/use-cases/index.html", + "safe_public_claims": [ + "page_brief is a browser-backed public showcase path" + ], + "forbidden_claims": [ + "page_brief is an equally release-proven baseline today" + ], + "missing_expected_artifacts": [ + "dedicated_healthy_proof_summary", + "dedicated_benchmark_summary", + "share_ready_recap" + ], + "assets": [] + } + ] +} diff --git a/docs/compatibility/index.html b/docs/compatibility/index.html index 9222d25..ac4c7e4 100644 --- a/docs/compatibility/index.html +++ b/docs/compatibility/index.html @@ -272,7 +272,7 @@

One truthful compatibility matrix for modern coding-agent teams.

@@ -363,8 +363,8 @@

Start with builder entrypoints

Proof

Start with use cases

-

If you need to prove the story to a team first, use the public proof-first loop and share-ready Workflow Case surfaces before deeper adoption.

-

Open use-case guide

+

If you need to prove the story to a team first, start with the public proof-first loop, the news_digest baseline, and the share-ready Workflow Case recap before deeper adoption.

+

See the first proven workflow

diff --git a/docs/governance/ui-button-coverage-matrix.md b/docs/governance/ui-button-coverage-matrix.md index ff1d180..0bf3f96 100644 --- a/docs/governance/ui-button-coverage-matrix.md +++ b/docs/governance/ui-button-coverage-matrix.md @@ -46,14 +46,14 @@ The `entries[]` schema in `button_inventory.{surface}.json` is: | id | surface | tier | file | action | test status | notes | evidence type | source path | source kind | source exists | |---|---|---|---|---|---|---|---|---|---|---| -| btn-dashboard-53ca4e3a73d9 | dashboard | P0 | apps/dashboard/app/agents/page.tsx:478 | View failed runs in bulk | TODO | Pending test mapping | | | | | +| btn-dashboard-e72b92a68a68 | dashboard | P0 | apps/dashboard/app/agents/page.tsx:519 | View failed runs in bulk | TODO | Pending test mapping | | | | | | btn-dashboard-84e1e3a0303b | dashboard | P0 | apps/dashboard/app/locks/page.tsx:212 | Go to runs | TODO | Pending test mapping | | | | | -| btn-dashboard-91f0b2a74a91 | dashboard | P0 | apps/dashboard/app/page.tsx:442 | View all runs | TODO | Pending test mapping | | | | | +| btn-dashboard-aa0e4d405a16 | dashboard | P0 | apps/dashboard/app/page.tsx:399 | View all runs | TODO | Pending test mapping | | | | | | btn-dashboard-0751a2cc97e1 | dashboard | P0 | apps/dashboard/app/pm/components/PMIntakeCenterPanel.tsx:171 | Button (`() => { if (isFirstSendReady) { onSend(); return;`) | COVERED | `apps/dashboard/tests/pm_intake_components_branches.test.tsx`(discover 阶段输入已就绪时直接触发首条需求发送) | mixed | apps/dashboard/tests/pm_intake_components_branches.test.tsx; scripts/e2e_pm_chat_command_tower_success.sh | real_playwright,unit_test | yes | -| btn-dashboard-17022684278f | dashboard | P0 | apps/dashboard/app/pm/components/PMIntakeCenterPanel.tsx:341 | Stop generation (`() => onStopGeneration()`) | TODO | Pending test mapping | | | | | -| btn-dashboard-065108a1157b | dashboard | P0 | apps/dashboard/app/pm/components/PMIntakeCenterPanel.tsx:353 | Send (`() => onSend()`) | TODO | Pending test mapping | | | | | +| btn-dashboard-57b043808d6e | dashboard | P0 | apps/dashboard/app/pm/components/PMIntakeCenterPanel.tsx:345 | Stop generation (`() => onStopGeneration()`) | TODO | Pending test mapping | | | | | +| btn-dashboard-bc8fb46f58f6 | dashboard | P0 | apps/dashboard/app/pm/components/PMIntakeCenterPanel.tsx:357 | Send (`() => onSend()`) | TODO | Pending test mapping | | | | | | btn-dashboard-1a24ee250060 | dashboard | P0 | apps/dashboard/app/pm/components/PMIntakeLeftSidebar.tsx:130 | Draft session (start typing) Focuses the composer only. Sending the first request creates the formal session. (`handleDraftSessionClick`) | TODO | Pending test mapping | | | | | -| btn-dashboard-83ad0d119d7a | dashboard | P0 | apps/dashboard/app/pm/components/PMIntakeRightSidebar.tsx:388 | Start execution (`() => onRun()`) | TODO | Pending test mapping | | | | | +| btn-dashboard-ce4e74573c55 | dashboard | P0 | apps/dashboard/app/pm/components/PMIntakeRightSidebar.tsx:496 | Start execution (`() => onRun()`) | TODO | Pending test mapping | | | | | | btn-dashboard-705b57a4622f | dashboard | P0 | apps/dashboard/app/runs/page.tsx:85 | Button | COVERED | `apps/dashboard/tests/home_page.test.tsx`(治理 CTA 在失败态 `打开全局失败治理` 与无失败态 `发起新任务` 两条分支断言) | mixed | apps/dashboard/tests/home_page.test.tsx; scripts/e2e_dashboard_high_risk_actions_real.sh | real_playwright,unit_test | yes | | btn-dashboard-bd7a9922f149 | dashboard | P0 | apps/dashboard/app/runs/page.tsx:89 | View failed events | TODO | Pending test mapping | | | | | | btn-dashboard-09f789e8c654 | dashboard | P0 | apps/dashboard/app/runs/page.tsx:98 | All | TODO | Pending test mapping | | | | | @@ -61,29 +61,30 @@ The `entries[]` schema in `button_inventory.{surface}.json` is: | btn-dashboard-1030c5ddb0ce | dashboard | P0 | apps/dashboard/app/runs/page.tsx:104 | Running | TODO | Pending test mapping | | | | | | btn-dashboard-cbefd14d7a48 | dashboard | P0 | apps/dashboard/app/runs/page.tsx:107 | Succeeded | TODO | Pending test mapping | | | | | | btn-dashboard-c30a4bb53012 | dashboard | P0 | apps/dashboard/app/search/page.tsx:107 | Button (`handlePromote`) | TODO | Pending test mapping | | | | | -| btn-dashboard-8fc092d821f7 | dashboard | P0 | apps/dashboard/components/DiffGatePanel.tsx:345 | Approve | TODO | Pending test mapping | | | | | -| btn-dashboard-f2739823f4be | dashboard | P0 | apps/dashboard/components/DiffGatePanel.tsx:348 | Reject change | TODO | Pending test mapping | | | | | -| btn-dashboard-e379a57d2f42 | dashboard | P0 | apps/dashboard/components/DiffGatePanel.tsx:351 | Rollback run | TODO | Pending test mapping | | | | | -| btn-dashboard-096bba28ddab | dashboard | P0 | apps/dashboard/components/DiffGatePanel.tsx:376 | Retry load (`() => void load({ forceNetwork: true`) | TODO | Pending test mapping | | | | | -| btn-dashboard-bc2c5a48d6bd | dashboard | P0 | apps/dashboard/components/DiffGatePanel.tsx:384 | Open runs list for investigation | TODO | Pending test mapping | | | | | -| btn-dashboard-51dc807b6856 | dashboard | P0 | apps/dashboard/components/DiffGatePanel.tsx:445 | Button (`() => void load({ soft: true, forceNetwork: true`) | TODO | Pending test mapping | | | | | -| btn-dashboard-32f4024dd485 | dashboard | P0 | apps/dashboard/components/DiffGatePanel.tsx:455 | Expand all ( ) (`() => { setVisibleCount(filteredItems.length); setListStatus(`Expanded all ${filteredItems.length`) | TODO | Pending test mapping | | | | | -| btn-dashboard-d9506dce130c | dashboard | P0 | apps/dashboard/components/DiffGatePanel.tsx:467 | Collapse to first 10 (`() => { setVisibleCount(DEFAULT_ROW_LIMIT); setListStatus(`Collapsed back to the first ${DEFAULT_ROW_LIMIT`) | TODO | Pending test mapping | | | | | -| btn-dashboard-3a7501fef195 | dashboard | P0 | apps/dashboard/components/DiffGatePanel.tsx:479 | Open runs list | TODO | Pending test mapping | | | | | -| btn-dashboard-26d2a744c8b5 | dashboard | P0 | apps/dashboard/components/DiffGatePanel.tsx:538 | Button (`() => void toggleDiff(runId)`) | TODO | Pending test mapping | | | | | -| btn-dashboard-17ff6f439a1f | dashboard | P0 | apps/dashboard/components/DiffGatePanel.tsx:548 | Rollback (`() => void handleRollback(runId)`) | TODO | Pending test mapping | | | | | -| btn-dashboard-09de387dc2d3 | dashboard | P0 | apps/dashboard/components/DiffGatePanel.tsx:559 | Reject change (`() => void handleReject(runId)`) | TODO | Pending test mapping | | | | | -| btn-dashboard-4e1b2cd04780 | dashboard | P0 | apps/dashboard/components/DiffGatePanel.tsx:612 | Collapse to first 10 (`() => setVisibleCount(DEFAULT_ROW_LIMIT)`) | TODO | Pending test mapping | | | | | +| btn-dashboard-22c59dc8ed30 | dashboard | P0 | apps/dashboard/app/workflows/WorkflowQueueMutationControls.tsx:168 | Button (`() => void handleQueueLatestRun()`) | TODO | Pending test mapping | | | | | +| btn-dashboard-82b252849785 | dashboard | P0 | apps/dashboard/app/workflows/WorkflowQueueMutationControls.tsx:176 | Button (`() => void handleRunNextQueue()`) | TODO | Pending test mapping | | | | | +| btn-dashboard-754a29e2c517 | dashboard | P0 | apps/dashboard/app/workflows/[id]/share/page.tsx:241 | Open latest run | TODO | Pending test mapping | | | | | +| btn-dashboard-336745456bd6 | dashboard | P0 | apps/dashboard/components/DiffGatePanel.tsx:346 | Approve | TODO | Pending test mapping | | | | | +| btn-dashboard-ae9db2db2e62 | dashboard | P0 | apps/dashboard/components/DiffGatePanel.tsx:349 | Reject change | TODO | Pending test mapping | | | | | +| btn-dashboard-4d435bdb23d1 | dashboard | P0 | apps/dashboard/components/DiffGatePanel.tsx:352 | Rollback run | TODO | Pending test mapping | | | | | +| btn-dashboard-e6ae160a418d | dashboard | P0 | apps/dashboard/components/DiffGatePanel.tsx:380 | Retry load (`() => void load({ forceNetwork: true`) | TODO | Pending test mapping | | | | | +| btn-dashboard-acfc94c64f3f | dashboard | P0 | apps/dashboard/components/DiffGatePanel.tsx:388 | Open runs list for investigation | TODO | Pending test mapping | | | | | +| btn-dashboard-6f2f6a51f5d6 | dashboard | P0 | apps/dashboard/components/DiffGatePanel.tsx:453 | Button (`() => void load({ soft: true, forceNetwork: true`) | TODO | Pending test mapping | | | | | +| btn-dashboard-ce0afe75ba10 | dashboard | P0 | apps/dashboard/components/DiffGatePanel.tsx:463 | Expand all ( ) (`() => { setVisibleCount(filteredItems.length); setListStatus(`Expanded all ${filteredItems.length`) | TODO | Pending test mapping | | | | | +| btn-dashboard-05d3bc1fafb2 | dashboard | P0 | apps/dashboard/components/DiffGatePanel.tsx:475 | Collapse to first 10 (`() => { setVisibleCount(DEFAULT_ROW_LIMIT); setListStatus(`Collapsed back to the first ${DEFAULT_ROW_LIMIT`) | TODO | Pending test mapping | | | | | +| btn-dashboard-684a1f37ae84 | dashboard | P0 | apps/dashboard/components/DiffGatePanel.tsx:487 | Open runs list | TODO | Pending test mapping | | | | | +| btn-dashboard-c346b771c8fd | dashboard | P0 | apps/dashboard/components/DiffGatePanel.tsx:546 | Button (`() => void toggleDiff(runId)`) | TODO | Pending test mapping | | | | | +| btn-dashboard-e7ce185be7e3 | dashboard | P0 | apps/dashboard/components/DiffGatePanel.tsx:556 | Rollback (`() => void handleRollback(runId)`) | TODO | Pending test mapping | | | | | +| btn-dashboard-11931a067db0 | dashboard | P0 | apps/dashboard/components/DiffGatePanel.tsx:567 | Reject change (`() => void handleReject(runId)`) | TODO | Pending test mapping | | | | | +| btn-dashboard-37327536b9d0 | dashboard | P0 | apps/dashboard/components/DiffGatePanel.tsx:620 | Collapse to first 10 (`() => setVisibleCount(DEFAULT_ROW_LIMIT)`) | TODO | Pending test mapping | | | | | | btn-dashboard-85adbbd51378 | dashboard | P0 | apps/dashboard/components/DiffViewer.tsx:193 | Refresh this page (`handleRetry`) | TODO | Pending test mapping | | | | | | btn-dashboard-f76ce6b00fbc | dashboard | P0 | apps/dashboard/components/DiffViewer.tsx:209 | Retry refresh (`handleRetry`) | TODO | Pending test mapping | | | | | -| btn-dashboard-13fbefe83564 | dashboard | P0 | apps/dashboard/components/GodModePanel.tsx:389 | Button (`(event) => requestApproveItem(String(item.run_id), event.currentTarget)`) | COVERED | `scripts/e2e_dashboard_high_risk_actions_real.sh` (GodMode 队列 我已完成继续执行) | real_playwright | scripts/e2e_dashboard_high_risk_actions_real.sh | real_playwright | yes | -| btn-dashboard-7c4f768a364b | dashboard | P0 | apps/dashboard/components/GodModePanel.tsx:418 | Button (`handleApprove`) | COVERED | `scripts/e2e_dashboard_high_risk_actions_real.sh` (手动输入 run_id 后 POST `/api/god-mode/approve`) | real_playwright | scripts/e2e_dashboard_high_risk_actions_real.sh | real_playwright | yes | -| btn-dashboard-8e33c759dd16 | dashboard | P0 | apps/dashboard/components/GodModePanel.tsx:443 | Confirm approval (`confirmApproveItem`) | TODO | Pending test mapping | | | | | -| btn-dashboard-ccf502a58012 | dashboard | P0 | apps/dashboard/components/RunList.tsx:42 | Create your first task in PM | TODO | Pending test mapping | | | | | -| btn-dashboard-7fcdbe2fe06d | dashboard | P0 | apps/dashboard/components/RunList.tsx:118 | Open triage | TODO | Pending test mapping | | | | | -| btn-dashboard-4ab8af9291f6 | dashboard | P0 | apps/dashboard/components/command-tower/CommandTowerHomeLayout.tsx:185 | Review runs | TODO | Pending test mapping | | | | | -| btn-dashboard-7d69e02f65e3 | dashboard | P0 | apps/dashboard/components/command-tower/CommandTowerHomeLayout.tsx:222 | Button (`() => props.onRunQuickAction?.("live")`) | COVERED | `apps/dashboard/tests/command_tower_priority_layout.suite.tsx`(Live Lane 快捷动作按钮可见) + `scripts/ui_full_e2e_gemini_audit.py`(`local_command_tower_strict_20260309_c` 真实点击通过) | mixed | apps/dashboard/tests/command_tower_priority_layout.suite.tsx; scripts/ui_full_e2e_gemini_audit.py | real_playwright,component_test | yes | -| btn-dashboard-c93ff33f37a5 | dashboard | P0 | apps/dashboard/components/command-tower/CommandTowerHomeLayout.tsx:274 | Review runs | TODO | Pending test mapping | | | | | +| btn-dashboard-cb30d228a1a2 | dashboard | P0 | apps/dashboard/components/GodModePanel.tsx:397 | Button (`(event) => requestApproveItem(String(item.run_id), event.currentTarget)`) | TODO | Pending test mapping | | | | | +| btn-dashboard-d6ba69913290 | dashboard | P0 | apps/dashboard/components/GodModePanel.tsx:426 | Button (`handleApprove`) | TODO | Pending test mapping | | | | | +| btn-dashboard-d99fa05ee83b | dashboard | P0 | apps/dashboard/components/GodModePanel.tsx:451 | Button (`confirmApproveItem`) | TODO | Pending test mapping | | | | | +| btn-dashboard-0bb0d449acbb | dashboard | P0 | apps/dashboard/components/RunList.tsx:38 | Create your first task in PM | TODO | Pending test mapping | | | | | +| btn-dashboard-9a51ea49f3ad | dashboard | P0 | apps/dashboard/components/RunList.tsx:114 | Open triage | TODO | Pending test mapping | | | | | +| btn-dashboard-218b91619ce6 | dashboard | P0 | apps/dashboard/components/command-tower/CommandTowerHomeLayout.tsx:230 | Button (`() => props.onRunQuickAction?.("live")`) | TODO | Pending test mapping | | | | | | btn-dashboard-43799d0e18bf | dashboard | P0 | apps/dashboard/components/command-tower/CommandTowerSessionDrawer.tsx:183 | Jump to latest run | TODO | Pending test mapping | | | | | | btn-dashboard-afcd85dc666d | dashboard | P0 | apps/dashboard/components/command-tower/CommandTowerSessionDrawer.tsx:221 | Button (`() => void props.handleSendMessage()`) | COVERED | `apps/dashboard/tests/command_tower_async_session.suite.tsx`(drawer 发送消息交互) | mixed | apps/dashboard/tests/command_tower_async_session.suite.tsx; scripts/e2e_dashboard_high_risk_actions_real.sh | real_playwright,component_test | yes | | btn-dashboard-dacf3324330b | dashboard | P0 | apps/dashboard/components/command-tower/CommandTowerSessionLive.tsx:391 | Open run detail (`() => void handleOpenRunDetail()`) | TODO | Pending test mapping | | | | | @@ -91,19 +92,19 @@ The `entries[]` schema in `button_inventory.{surface}.json` is: | btn-dashboard-ed5804bc06e8 | dashboard | P0 | apps/dashboard/components/command-tower/CommandTowerSessionLive.tsx:436 | Go to the PM session and trigger /run | TODO | Pending test mapping | | | | | | btn-dashboard-76d73477e1fb | dashboard | P0 | apps/dashboard/components/command-tower/CommandTowerSessionLive.tsx:443 | Retry (`() => void handleOpenRunDetail()`) | TODO | Pending test mapping | | | | | | btn-dashboard-76d75b2a6b17 | dashboard | P0 | apps/dashboard/components/command-tower/CommandTowerSessionLive.tsx:461 | Runs (`() => handleSessionMainTabClick("runs")`) | TODO | Pending test mapping | | | | | -| btn-dashboard-87f1f12afef6 | dashboard | P0 | apps/dashboard/components/run-detail/RunDetailDetailPanel.tsx:188 | Diff (`() => onTabChange("diff")`) | TODO | Pending test mapping | | | | | -| btn-dashboard-37316aa9c761 | dashboard | P0 | apps/dashboard/components/run-detail/RunDetailDetailPanel.tsx:201 | Logs (`() => onTabChange("logs")`) | TODO | Pending test mapping | | | | | -| btn-dashboard-2b7498d35c6b | dashboard | P0 | apps/dashboard/components/run-detail/RunDetailDetailPanel.tsx:214 | Reports (`() => onTabChange("reports")`) | TODO | Pending test mapping | | | | | -| btn-dashboard-ea6b56eb4d31 | dashboard | P0 | apps/dashboard/components/run-detail/RunDetailDetailPanel.tsx:228 | Chain (`() => onTabChange("chain")`) | TODO | Pending test mapping | | | | | -| btn-dashboard-fe401827e13a | dashboard | P0 | apps/dashboard/components/run-detail/RunDetailDetailPanel.tsx:332 | Run replay comparison (`onReplay`) | TODO | Pending test mapping | | | | | -| btn-dashboard-0b19aea4c7fc | dashboard | P0 | apps/dashboard/components/run-detail/RunDetailStatusContractCard.tsx:107 | Button (`onToggleLive`) | COVERED | `scripts/e2e_dashboard_high_risk_actions_real.sh` (RunDetail 实时开关) | real_playwright | scripts/e2e_dashboard_high_risk_actions_real.sh | real_playwright | yes | -| btn-dashboard-58f359e9c622 | dashboard | P0 | apps/dashboard/components/run-detail/RunDetailStatusContractCard.tsx:126 | Open diagnostic report (`onOpenReports`) | TODO | Pending test mapping | | | | | -| btn-dashboard-0508cb57c8a2 | dashboard | P0 | apps/dashboard/components/run-detail/RunDetailStatusContractCard.tsx:129 | Open execution logs (`onOpenLogs`) | TODO | Pending test mapping | | | | | -| btn-dashboard-378abe0a476f | dashboard | P0 | apps/dashboard/components/run-detail/RunDetailStatusContractCard.tsx:162 | Open manual approvals | TODO | Pending test mapping | | | | | -| btn-dashboard-09f15ee3b00d | dashboard | P1 | apps/dashboard/app/agents/page.tsx:455 | Apply filter | TODO | Pending test mapping | | | | | -| btn-dashboard-0e506dd23fb3 | dashboard | P1 | apps/dashboard/app/agents/page.tsx:459 | Clear filter | TODO | Pending test mapping | | | | | +| btn-dashboard-6222f438b650 | dashboard | P0 | apps/dashboard/components/run-detail/RunDetailDetailPanel.tsx:194 | Diff (`() => onTabChange("diff")`) | TODO | Pending test mapping | | | | | +| btn-dashboard-5c6aa626e891 | dashboard | P0 | apps/dashboard/components/run-detail/RunDetailDetailPanel.tsx:207 | Logs (`() => onTabChange("logs")`) | TODO | Pending test mapping | | | | | +| btn-dashboard-781c337007cf | dashboard | P0 | apps/dashboard/components/run-detail/RunDetailDetailPanel.tsx:220 | Reports (`() => onTabChange("reports")`) | TODO | Pending test mapping | | | | | +| btn-dashboard-2050e9c3e23a | dashboard | P0 | apps/dashboard/components/run-detail/RunDetailDetailPanel.tsx:234 | Chain (`() => onTabChange("chain")`) | TODO | Pending test mapping | | | | | +| btn-dashboard-259f58f9be93 | dashboard | P0 | apps/dashboard/components/run-detail/RunDetailDetailPanel.tsx:341 | Run replay comparison (`onReplay`) | TODO | Pending test mapping | | | | | +| btn-dashboard-3200d9f3343f | dashboard | P0 | apps/dashboard/components/run-detail/RunDetailStatusContractCard.tsx:117 | Button (`onToggleLive`) | TODO | Pending test mapping | | | | | +| btn-dashboard-15901e65ac1c | dashboard | P0 | apps/dashboard/components/run-detail/RunDetailStatusContractCard.tsx:136 | Open diagnostic report (`onOpenReports`) | TODO | Pending test mapping | | | | | +| btn-dashboard-47878e9d3cdb | dashboard | P0 | apps/dashboard/components/run-detail/RunDetailStatusContractCard.tsx:139 | Open execution logs (`onOpenLogs`) | TODO | Pending test mapping | | | | | +| btn-dashboard-fcd5ca7b5b8d | dashboard | P0 | apps/dashboard/components/run-detail/RunDetailStatusContractCard.tsx:172 | Open manual approvals | TODO | Pending test mapping | | | | | +| btn-dashboard-6daf943ceca1 | dashboard | P1 | apps/dashboard/app/agents/page.tsx:496 | Apply filter | TODO | Pending test mapping | | | | | +| btn-dashboard-e1744eb1fc69 | dashboard | P1 | apps/dashboard/app/agents/page.tsx:500 | Clear filter | TODO | Pending test mapping | | | | | | btn-dashboard-4ebab1ed4dd0 | dashboard | P1 | apps/dashboard/app/command-tower/error.tsx:25 | Retry load (`reset`) | TODO | Pending test mapping | | | | | -| btn-dashboard-7765fefc00b9 | dashboard | P1 | apps/dashboard/app/contracts/page.tsx:65 | Apply filter | TODO | Pending test mapping | | | | | +| btn-dashboard-022f20539980 | dashboard | P1 | apps/dashboard/app/contracts/page.tsx:61 | Apply filter | TODO | Pending test mapping | | | | | | btn-dashboard-82f43a564b45 | dashboard | P1 | apps/dashboard/app/error.tsx:25 | Retry load (`reset`) | TODO | Pending test mapping | | | | | | btn-dashboard-c87834c3d7eb | dashboard | P1 | apps/dashboard/app/events/page.tsx:195 | Apply filter | TODO | Pending test mapping | | | | | | btn-dashboard-d47c92fe1a7d | dashboard | P1 | apps/dashboard/app/events/page.tsx:196 | Clear filter | TODO | Pending test mapping | | | | | @@ -113,97 +114,103 @@ The `entries[]` schema in `button_inventory.{surface}.json` is: | btn-dashboard-a6debf056dda | dashboard | P1 | apps/dashboard/app/reviews/page.tsx:238 | Apply filters | TODO | Pending test mapping | | | | | | btn-dashboard-72a02803713e | dashboard | P1 | apps/dashboard/app/tests/page.tsx:155 | Apply filter | TODO | Pending test mapping | | | | | | btn-dashboard-05fc70388167 | dashboard | P1 | apps/dashboard/app/tests/page.tsx:156 | Clear filter | TODO | Pending test mapping | | | | | +| btn-dashboard-5911f493d52b | dashboard | P1 | apps/dashboard/components/DashboardShellChrome.tsx:61 | Button (`() => { setLocale((previous) => { const next = toggleUiLocale(previous); persistPreferredUiLocale(next); router.refre...`) | TODO | Pending test mapping | | | | | | btn-dashboard-43e59965bb9f | dashboard | P1 | apps/dashboard/components/EventTimeline.tsx:107 | Button (`() => setFilter(preset.value)`) | COVERED | `apps/dashboard/tests/event_timeline.test.tsx` (filter preset buttons) | unit_test | apps/dashboard/tests/event_timeline.test.tsx | unit_test | yes | | btn-dashboard-694553a4e310 | dashboard | P1 | apps/dashboard/components/EventTimeline.tsx:147 | Clear filters (`() => setFilter("")`) | TODO | Pending test mapping | | | | | | btn-dashboard-ef5fa7839de8 | dashboard | P1 | apps/dashboard/components/EventTimeline.tsx:163 | void loadPending({ trigger: "refresh"`) | TODO | Pending test mapping | | | | | +| btn-dashboard-9e960e9ecc89 | dashboard | P1 | apps/dashboard/components/command-tower/CommandTowerHomeDrawer.tsx:245 | Button (`onApplyFilters`) | TODO | Pending test mapping | | | | | +| btn-dashboard-f454fbd51a7f | dashboard | P1 | apps/dashboard/components/command-tower/CommandTowerHomeDrawer.tsx:254 | Button (`onResetFilters`) | TODO | Pending test mapping | | | | | +| btn-dashboard-75c7a062adb9 | dashboard | P1 | apps/dashboard/components/command-tower/CommandTowerHomeLayout.tsx:135 | Button (`props.toggleHighRiskFocus`) | TODO | Pending test mapping | | | | | +| btn-dashboard-11f6cc17c02a | dashboard | P1 | apps/dashboard/components/command-tower/CommandTowerHomeLayout.tsx:235 | Button (`props.toggleHighRiskFocus`) | TODO | Pending test mapping | | | | | +| btn-dashboard-854e7bd60f27 | dashboard | P1 | apps/dashboard/components/command-tower/CommandTowerHomeLayout.tsx:266 | Button (`props.resetFilters`) | TODO | Pending test mapping | | | | | | btn-dashboard-8fa7ec44694f | dashboard | P1 | apps/dashboard/components/command-tower/CommandTowerSessionDrawer.tsx:88 | Button (`props.handleToggleLive`) | COVERED | `apps/dashboard/tests/command_tower_async_session.suite.tsx`(session live toggle) | component_test | apps/dashboard/tests/command_tower_async_session.suite.tsx | component_test | yes | | btn-dashboard-7ee1d15e3d34 | dashboard | P1 | apps/dashboard/components/command-tower/CommandTowerSessionDrawer.tsx:99 | Manual refresh (`() => void props.handleManualRefresh()`) | TODO | Pending test mapping | | | | | | btn-dashboard-c577de4c64a6 | dashboard | P1 | apps/dashboard/components/command-tower/CommandTowerSessionLive.tsx:369 | Refresh latest progress (`() => void handleManualRefresh()`) | TODO | Pending test mapping | | | | | | btn-dashboard-9f247d1c3721 | dashboard | P1 | apps/dashboard/components/command-tower/CommandTowerSessionLive.tsx:380 | Button (`handleToggleLive`) | COVERED | `scripts/e2e_command_tower_controls_real.sh` (Session 暂停/恢复实时) | real_playwright | scripts/e2e_command_tower_controls_real.sh | real_playwright | yes | | btn-dashboard-1b2e8b0293b2 | dashboard | P1 | apps/dashboard/components/command-tower/CommandTowerSessionLive.tsx:478 | Role flow (`() => handleSessionMainTabClick("graph")`) | TODO | Pending test mapping | | | | | | btn-dashboard-a0af236fb9a3 | dashboard | P1 | apps/dashboard/components/command-tower/CommandTowerSessionLive.tsx:495 | Timeline (`() => handleSessionMainTabClick("timeline")`) | TODO | Pending test mapping | | | | | +| btn-dashboard-a48e73885017 | dashboard | P1 | apps/dashboard/components/workflows/WorkflowCaseShareActions.tsx:59 | Copy share link (`() => void copyShareLink()`) | TODO | Pending test mapping | | | | | | btn-desktop-9a21b868022d | desktop | P0 | apps/desktop/src/components/chain/NodeDetailDrawer.tsx:48 | Open diff review (`onOpenDiff`) | TODO | Pending test mapping | | | | | -| btn-desktop-bbe09c32360d | desktop | P0 | apps/desktop/src/components/conversation/ChatPanel.tsx:310 | Send message (`onComposerEnterSend`) | TODO | Pending test mapping | | | | | -| btn-desktop-6de53f64743e | desktop | P0 | apps/desktop/src/components/conversation/ChatPanel.tsx:323 | Stop generation (`stopGeneration`) | TODO | Pending test mapping | | | | | +| btn-desktop-6828223c0bac | desktop | P0 | apps/desktop/src/components/conversation/ChatPanel.tsx:485 | Send message (`onComposerEnterSend`) | TODO | Pending test mapping | | | | | +| btn-desktop-adaf450b30d3 | desktop | P0 | apps/desktop/src/components/conversation/ChatPanel.tsx:498 | Stop generation (`stopGeneration`) | TODO | Pending test mapping | | | | | | btn-desktop-4079d0fc610c | desktop | P0 | apps/desktop/src/components/review/DiffReviewModal.tsx:98 | × (`onClose`) | TODO | Pending test mapping | | | | | | btn-desktop-91fabc0723b0 | desktop | P0 | apps/desktop/src/components/review/DiffReviewModal.tsx:114 | Accept and merge (`onAccept`) | TODO | Pending test mapping | | | | | | btn-desktop-fde31493ffd3 | desktop | P0 | apps/desktop/src/components/review/DiffReviewModal.tsx:115 | Request changes (`onRework`) | TODO | Pending test mapping | | | | | | btn-desktop-ffb5408aabe4 | desktop | P0 | apps/desktop/src/lib/desktopUi.tsx:299 | View full diff (`() => reportActions?.onViewDiff?.(embed.id)`) | TODO | Pending test mapping | | | | | | btn-desktop-a88f7e08ed01 | desktop | P0 | apps/desktop/src/pages/CTSessionDetailPage.tsx:482 | Button (`handleSend`) | TODO | Pending test mapping | | | | | -| btn-desktop-acb28dc8d17f | desktop | P0 | apps/desktop/src/pages/ChangeGatesPage.tsx:83 | Button (`() => loadDiff(runId)`) | COVERED | `apps/desktop/src/pages/desktop_p0_misc_controls.test.tsx` + `apps/desktop/src/pages/run_detail_page_controls.test.tsx` | mixed | apps/desktop/src/pages/desktop_p0_misc_controls.test.tsx; apps/desktop/src/pages/run_detail_page_controls.test.tsx; scripts/e2e_desktop_high_risk_actions_real.sh | real_playwright,unit_test | yes | -| btn-desktop-43d16bbcce69 | desktop | P0 | apps/desktop/src/pages/ChangeGatesPage.tsx:86 | Rollback (`() => handleAction(runId, "rollback")`) | TODO | Pending test mapping | | | | | -| btn-desktop-47177eb441e8 | desktop | P0 | apps/desktop/src/pages/ChangeGatesPage.tsx:87 | Reject change (`() => handleAction(runId, "reject")`) | TODO | Pending test mapping | | | | | -| btn-desktop-d9bd7f4f7d1f | desktop | P0 | apps/desktop/src/pages/CommandTowerPage.tsx:488 | {opt.value === "all" && } {opt.value === "high_risk" && } {opt.value === "blocked" && } {opt.value === "running" && } (`() => setFocusMode(opt.value)`) | TODO | Pending test mapping | | | | | -| btn-desktop-286fa5fcd60b | desktop | P0 | apps/desktop/src/pages/GodModePage.tsx:158 | Approve execution (`() => openConfirmDialog(runId)`) | TODO | Pending test mapping | | | | | -| btn-desktop-a2dc86959d1a | desktop | P0 | apps/desktop/src/pages/GodModePage.tsx:171 | Approve (`handleManualApprove`) | TODO | Pending test mapping | | | | | -| btn-desktop-3abe2b1a2b5e | desktop | P0 | apps/desktop/src/pages/GodModePage.tsx:179 | Confirm approval Approve run ? This action cannot be undone. Cancel (`closeConfirmDialog`) | TODO | Pending test mapping | | | | | -| btn-desktop-a21b01d43400 | desktop | P0 | apps/desktop/src/pages/GodModePage.tsx:190 | Confirm approval (`() => handleApprove(confirmRunId)`) | TODO | Pending test mapping | | | | | -| btn-desktop-96260b1f689c | desktop | P0 | apps/desktop/src/pages/OverviewPage.tsx:242 | View all runs (`() => onNavigate("runs")`) | TODO | Pending test mapping | | | | | -| btn-desktop-d6601271781e | desktop | P0 | apps/desktop/src/pages/OverviewPage.tsx:256 | Button (`() => onNavigateToRun(run.run_id)`) | TODO | Pending test mapping | | | | | -| btn-desktop-ab923b060b9d | desktop | P0 | apps/desktop/src/pages/OverviewPage.tsx:303 | View Run (`() => onNavigateToRun(entry.runId)`) | TODO | Pending test mapping | | | | | -| btn-desktop-208a8c2b84be | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:331 | Retry load (`() => void load()`) | TODO | Pending test mapping | | | | | -| btn-desktop-9fdc16319c64 | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:332 | Back to list (`onBack`) | TODO | Pending test mapping | | | | | -| btn-desktop-6e9ca028419a | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:342 | Retry load (`() => void load()`) | TODO | Pending test mapping | | | | | -| btn-desktop-46878b6a5438 | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:343 | Back to list (`onBack`) | TODO | Pending test mapping | | | | | -| btn-desktop-3eaeed5d6d21 | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:407 | Back to list (`onBack`) | TODO | Pending test mapping | | | | | -| btn-desktop-f142f55adaf8 | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:413 | Button (`() => setLiveEnabled(p => !p)`) | TODO | Pending test mapping | | | | | -| btn-desktop-e82780cc0602 | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:463 | Retry fetch (`() => void load()`) | TODO | Pending test mapping | | | | | -| btn-desktop-88c9516fb5e7 | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:508 | Refresh data (`() => void load()`) | TODO | Pending test mapping | | | | | -| btn-desktop-f7fea8f4e637 | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:512 | Promote evidence (`() => handleAction("promote")`) | TODO | Pending test mapping | | | | | -| btn-desktop-53667fd0e9a5 | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:520 | Rollback (`() => handleAction("rollback")`) | TODO | Pending test mapping | | | | | -| btn-desktop-7a33361df241 | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:521 | Reject (`() => handleAction("reject")`) | TODO | Pending test mapping | | | | | -| btn-desktop-9f246d58d70c | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:522 | Refresh (`load`) | TODO | Pending test mapping | | | | | -| btn-desktop-b51d491c90c0 | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:528 | Button (`() => setActiveTab(tab.key)`) | TODO | Pending test mapping | | | | | -| btn-desktop-96262083a24e | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:541 | Refresh events (`() => void load()`) | TODO | Pending test mapping | | | | | -| btn-desktop-12e22325ec21 | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:555 | Button (`() => toggleExpandedEvent(i)`) | TODO | Pending test mapping | | | | | -| btn-desktop-44624472fa53 | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:591 | Retry load (`() => void load()`) | TODO | Pending test mapping | | | | | -| btn-desktop-68dff775f18d | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:592 | Back to Event timeline (`() => setActiveTab("events")`) | TODO | Pending test mapping | | | | | -| btn-desktop-ad14a0fabe27 | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:614 | Refresh reports (`() => void load()`) | TODO | Pending test mapping | | | | | -| btn-desktop-2e9c21b5173e | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:636 | Refresh tool calls (`() => void load()`) | TODO | Pending test mapping | | | | | -| btn-desktop-350b36d41664 | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:679 | Refresh chain flow (`() => void load()`) | TODO | Pending test mapping | | | | | -| btn-desktop-30ad9f1979f5 | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:694 | Refresh contract (`() => void load()`) | TODO | Pending test mapping | | | | | -| btn-desktop-6d2e10402612 | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:715 | Run replay (`() => handleAction("replay")`) | TODO | Pending test mapping | | | | | +| btn-desktop-9fe2e2dc3a41 | desktop | P0 | apps/desktop/src/pages/ChangeGatesPage.tsx:86 | Button (`() => loadDiff(runId)`) | TODO | Pending test mapping | | | | | +| btn-desktop-94724af6495f | desktop | P0 | apps/desktop/src/pages/ChangeGatesPage.tsx:89 | Rollback (`() => handleAction(runId, "rollback")`) | TODO | Pending test mapping | | | | | +| btn-desktop-a9647c780426 | desktop | P0 | apps/desktop/src/pages/ChangeGatesPage.tsx:90 | Reject change (`() => handleAction(runId, "reject")`) | TODO | Pending test mapping | | | | | +| btn-desktop-9cb742e43036 | desktop | P0 | apps/desktop/src/pages/CommandTowerPage.tsx:494 | {opt.value === "all" && } {opt.value === "high_risk" && } {opt.value === "blocked" && } {opt.value === "running" && } (`() => setFocusMode(opt.value)`) | TODO | Pending test mapping | | | | | +| btn-desktop-ec99e080c351 | desktop | P0 | apps/desktop/src/pages/GodModePage.tsx:233 | Button (`() => openConfirmDialog(runId)`) | TODO | Pending test mapping | | | | | +| btn-desktop-4125529b5827 | desktop | P0 | apps/desktop/src/pages/GodModePage.tsx:262 | Button (`handleManualApprove`) | TODO | Pending test mapping | | | | | +| btn-desktop-05478759b240 | desktop | P0 | apps/desktop/src/pages/GodModePage.tsx:290 | Button (`() => handleApprove(confirmRunId)`) | TODO | Pending test mapping | | | | | +| btn-desktop-64e767087f61 | desktop | P0 | apps/desktop/src/pages/OverviewPage.tsx:235 | Button (`() => onNavigate("runs")`) | TODO | Pending test mapping | | | | | +| btn-desktop-58c1e0cb3ad9 | desktop | P0 | apps/desktop/src/pages/OverviewPage.tsx:249 | Button (`() => onNavigateToRun(run.run_id)`) | TODO | Pending test mapping | | | | | +| btn-desktop-c5be35312e10 | desktop | P0 | apps/desktop/src/pages/OverviewPage.tsx:296 | Button (`() => onNavigateToRun(entry.runId)`) | TODO | Pending test mapping | | | | | +| btn-desktop-ca6302ae539f | desktop | P0 | apps/desktop/src/pages/RunComparePage.tsx:91 | Back (`onBack`) | TODO | Pending test mapping | | | | | +| btn-desktop-5e68b4638a6e | desktop | P0 | apps/desktop/src/pages/RunComparePage.tsx:96 | Back to run detail (`onBack`) | TODO | Pending test mapping | | | | | +| btn-desktop-1fc003fec67a | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:341 | Button (`() => void load()`) | TODO | Pending test mapping | | | | | +| btn-desktop-d34f9eaedcf2 | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:342 | Button (`onBack`) | TODO | Pending test mapping | | | | | +| btn-desktop-0c6fd4daeff9 | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:352 | Button (`() => void load()`) | TODO | Pending test mapping | | | | | +| btn-desktop-fd735710beff | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:353 | Button (`onBack`) | TODO | Pending test mapping | | | | | +| btn-desktop-e0d11350db7c | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:412 | Button (`onBack`) | TODO | Pending test mapping | | | | | +| btn-desktop-dea3d13bc103 | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:418 | Button (`() => setLiveEnabled(p => !p)`) | TODO | Pending test mapping | | | | | +| btn-desktop-6ec4f272a6ad | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:499 | Button (`() => void load()`) | TODO | Pending test mapping | | | | | +| btn-desktop-a8a82130a8aa | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:544 | Button (`() => void load()`) | TODO | Pending test mapping | | | | | +| btn-desktop-23d1b14efb1d | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:548 | Button (`() => handleAction("promote")`) | TODO | Pending test mapping | | | | | +| btn-desktop-ffb241c06bce | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:556 | Button (`() => handleAction("rollback")`) | TODO | Pending test mapping | | | | | +| btn-desktop-06e08ce41b8e | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:557 | Button (`() => handleAction("reject")`) | TODO | Pending test mapping | | | | | +| btn-desktop-228017131f69 | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:558 | Button (`load`) | TODO | Pending test mapping | | | | | +| btn-desktop-b4d37b3bf071 | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:564 | Button (`() => setActiveTab(tab.key)`) | TODO | Pending test mapping | | | | | +| btn-desktop-6d8e670816f8 | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:577 | Button (`() => void load()`) | TODO | Pending test mapping | | | | | +| btn-desktop-89d9ed8a0cc8 | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:591 | Button (`() => toggleExpandedEvent(i)`) | TODO | Pending test mapping | | | | | +| btn-desktop-2a92aff78f62 | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:627 | Button (`() => void load()`) | TODO | Pending test mapping | | | | | +| btn-desktop-72575d8e3c01 | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:628 | Button (`() => setActiveTab("events")`) | TODO | Pending test mapping | | | | | +| btn-desktop-963e98b8a362 | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:650 | Button (`() => void load()`) | TODO | Pending test mapping | | | | | +| btn-desktop-fd89eefbedf9 | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:672 | Button (`() => void load()`) | TODO | Pending test mapping | | | | | +| btn-desktop-023753c3b783 | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:715 | Button (`() => void load()`) | TODO | Pending test mapping | | | | | +| btn-desktop-47fc8349d0a2 | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:730 | Button (`() => void load()`) | TODO | Pending test mapping | | | | | +| btn-desktop-12f12609c0bd | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:751 | Button (`() => handleAction("replay")`) | TODO | Pending test mapping | | | | | +| btn-desktop-ad97eef4846c | desktop | P0 | apps/desktop/src/pages/RunDetailPage.tsx:795 | Button (`onOpenCompare`) | TODO | Pending test mapping | | | | | | btn-desktop-6688e04822a0 | desktop | P0 | apps/desktop/src/pages/RunsPage.tsx:54 | Refresh (`load`) | TODO | Pending test mapping | | | | | | btn-desktop-ec445f782463 | desktop | P0 | apps/desktop/src/pages/RunsPage.tsx:92 | Button (`() => onNavigateToRun(run.run_id)`) | TODO | Pending test mapping | | | | | | btn-desktop-f1c8ace8cf76 | desktop | P0 | apps/desktop/src/pages/SearchPage.tsx:105 | Promote to EvidenceBundle (`handlePromote`) | TODO | Pending test mapping | | | | | -| btn-desktop-5291fa7fe5ea | desktop | P0 | apps/desktop/src/pages/WorkflowDetailPage.tsx:39 | Button (`() => onNavigateToRun(r.run_id)`) | COVERED | `apps/desktop/src/pages/desktop_p0_misc_controls.test.tsx` + `apps/desktop/src/pages/run_detail_page_controls.test.tsx` | mixed | apps/desktop/src/pages/desktop_p0_misc_controls.test.tsx; apps/desktop/src/pages/run_detail_page_controls.test.tsx; scripts/e2e_desktop_high_risk_actions_real.sh | real_playwright,unit_test | yes | +| btn-desktop-b1036f47a77c | desktop | P0 | apps/desktop/src/pages/WorkflowDetailPage.tsx:153 | Button (`() => void handleQueueLatestRun()`) | TODO | Pending test mapping | | | | | +| btn-desktop-5bb2f78ee0f5 | desktop | P0 | apps/desktop/src/pages/WorkflowDetailPage.tsx:154 | Button (`() => void handleRunNextQueue()`) | TODO | Pending test mapping | | | | | +| btn-desktop-c2d8eae2216f | desktop | P0 | apps/desktop/src/pages/WorkflowDetailPage.tsx:228 | Button (`() => onNavigateToRun(r.run_id)`) | TODO | Pending test mapping | | | | | +| btn-desktop-bcf160d1dfe3 | desktop | P0 | apps/desktop/src/pages/WorkflowsPage.tsx:67 | Button (`() => void handleRunNextQueue()`) | TODO | Pending test mapping | | | | | +| btn-desktop-03cd05daf6a6 | desktop | P1 | apps/desktop/src/App.tsx:787 | Button (`() => { setUiLocale((previous) => { const next = toggleUiLocale(previous); persistPreferredUiLocale(next); return next;`) | TODO | Pending test mapping | | | | | | btn-desktop-98c57c960d8a | desktop | P1 | apps/desktop/src/components/chain/NodeDetailDrawer.tsx:45 | Button (`onToggleRaw`) | COVERED | `apps/desktop/src/pages/desktop_p1_controls.test.tsx` + `apps/desktop/src/App.test.tsx` | unit_test | apps/desktop/src/pages/desktop_p1_controls.test.tsx; apps/desktop/src/App.test.tsx | unit_test | yes | -| btn-desktop-99c6e81edb93 | desktop | P1 | apps/desktop/src/components/conversation/ChatPanel.tsx:183 | Refresh now (`() => refreshNow()`) | TODO | Pending test mapping | | | | | +| btn-desktop-70d50c26f366 | desktop | P1 | apps/desktop/src/components/conversation/ChatPanel.tsx:287 | Refresh now (`() => refreshNow()`) | TODO | Pending test mapping | | | | | | btn-desktop-cce4528a6f6d | desktop | P1 | apps/desktop/src/pages/CTSessionDetailPage.tsx:350 | Button (`() => setLiveEnabled((p) => !p)`) | TODO | Pending test mapping | | | | | | btn-desktop-ab7b2f366eab | desktop | P1 | apps/desktop/src/pages/CTSessionDetailPage.tsx:351 | Refresh now (`() => void refreshAll()`) | TODO | Pending test mapping | | | | | | btn-desktop-8f8b537fedcd | desktop | P1 | apps/desktop/src/pages/ChangeGatesPage.tsx:45 | Refresh (`load`) | TODO | Pending test mapping | | | | | -| btn-desktop-e059b3505731 | desktop | P1 | apps/desktop/src/pages/CommandTowerPage.tsx:399 | Button (`refreshNow`) | TODO | Pending test mapping | | | | | -| btn-desktop-30d56f85577e | desktop | P1 | apps/desktop/src/pages/CommandTowerPage.tsx:402 | Button (`() => setLiveEnabled((p) => !p)`) | TODO | Pending test mapping | | | | | -| btn-desktop-13c04f88da47 | desktop | P1 | apps/desktop/src/pages/CommandTowerPage.tsx:479 | Apply (`applyFilters`) | TODO | Pending test mapping | | | | | -| btn-desktop-8490c467b104 | desktop | P1 | apps/desktop/src/pages/CommandTowerPage.tsx:480 | Reset (`resetFilters`) | TODO | Pending test mapping | | | | | -| btn-desktop-0acf6574bc77 | desktop | P1 | apps/desktop/src/pages/CommandTowerPage.tsx:517 | Button (`refreshNow`) | TODO | Pending test mapping | | | | | -| btn-desktop-9985a70dd53a | desktop | P1 | apps/desktop/src/pages/CommandTowerPage.tsx:520 | Pause live triage (`() => setLiveEnabled(false)`) | TODO | Pending test mapping | | | | | -| btn-desktop-23dbd10a6847 | desktop | P1 | apps/desktop/src/pages/CommandTowerPage.tsx:528 | Reset filters (`resetFilters`) | TODO | Pending test mapping | | | | | -| btn-desktop-b7125a05d88c | desktop | P1 | apps/desktop/src/pages/CommandTowerPage.tsx:551 | Button (`refreshNow`) | TODO | Pending test mapping | | | | | -| btn-desktop-ddf278b1a249 | desktop | P1 | apps/desktop/src/pages/CommandTowerPage.tsx:554 | View all sessions (`() => { setFocusMode("all"); resetFilters();`) | TODO | Pending test mapping | | | | | -| btn-desktop-e6f1670302a4 | desktop | P1 | apps/desktop/src/pages/CommandTowerPage.tsx:648 | Alt+Shift+R (`refreshNow`) | TODO | Pending test mapping | | | | | -| btn-desktop-122fe3791d87 | desktop | P1 | apps/desktop/src/pages/CommandTowerPage.tsx:649 | Alt+Shift+L (`() => setLiveEnabled((p) => !p)`) | TODO | Pending test mapping | | | | | -| btn-desktop-d8c9de8bf867 | desktop | P1 | apps/desktop/src/pages/CommandTowerPage.tsx:650 | Export Alt+Shift+E (`exportFailedSessions`) | TODO | Pending test mapping | | | | | -| btn-desktop-79104fb723f0 | desktop | P1 | apps/desktop/src/pages/CommandTowerPage.tsx:651 | Copy Alt+Shift+C (`() => void copyCurrentViewLink()`) | TODO | Pending test mapping | | | | | -| btn-desktop-76fdc780f2f5 | desktop | P1 | apps/desktop/src/pages/CommandTowerPage.tsx:702 | Button (`refreshNow`) | TODO | Pending test mapping | | | | | -| btn-desktop-0dbb31d07849 | desktop | P1 | apps/desktop/src/pages/ContractsPage.tsx:20 | Refresh (`load`) | TODO | Pending test mapping | | | | | +| btn-desktop-17285ab7497c | desktop | P1 | apps/desktop/src/pages/CommandTowerPage.tsx:405 | Button (`refreshNow`) | TODO | Pending test mapping | | | | | +| btn-desktop-a6685d58cfef | desktop | P1 | apps/desktop/src/pages/CommandTowerPage.tsx:408 | Button (`() => setLiveEnabled((p) => !p)`) | TODO | Pending test mapping | | | | | +| btn-desktop-a564bdc75dff | desktop | P1 | apps/desktop/src/pages/CommandTowerPage.tsx:485 | Button (`applyFilters`) | TODO | Pending test mapping | | | | | +| btn-desktop-0e513ea1f298 | desktop | P1 | apps/desktop/src/pages/CommandTowerPage.tsx:486 | Button (`resetFilters`) | TODO | Pending test mapping | | | | | +| btn-desktop-ed5032000f2f | desktop | P1 | apps/desktop/src/pages/CommandTowerPage.tsx:523 | Button (`refreshNow`) | TODO | Pending test mapping | | | | | +| btn-desktop-dcd1a1a6468d | desktop | P1 | apps/desktop/src/pages/CommandTowerPage.tsx:526 | Button (`() => setLiveEnabled(false)`) | TODO | Pending test mapping | | | | | +| btn-desktop-3378d622ef9a | desktop | P1 | apps/desktop/src/pages/CommandTowerPage.tsx:534 | Button (`resetFilters`) | TODO | Pending test mapping | | | | | +| btn-desktop-1027a5d86943 | desktop | P1 | apps/desktop/src/pages/CommandTowerPage.tsx:557 | Button (`refreshNow`) | TODO | Pending test mapping | | | | | +| btn-desktop-6c1bbf9edfaf | desktop | P1 | apps/desktop/src/pages/CommandTowerPage.tsx:560 | Button (`() => { setFocusMode("all"); resetFilters();`) | TODO | Pending test mapping | | | | | +| btn-desktop-1ce62f0c4ff4 | desktop | P1 | apps/desktop/src/pages/CommandTowerPage.tsx:654 | Alt+Shift+R (`refreshNow`) | TODO | Pending test mapping | | | | | +| btn-desktop-0d902621b5f8 | desktop | P1 | apps/desktop/src/pages/CommandTowerPage.tsx:655 | Alt+Shift+L (`() => setLiveEnabled((p) => !p)`) | TODO | Pending test mapping | | | | | +| btn-desktop-123b9b1bc126 | desktop | P1 | apps/desktop/src/pages/CommandTowerPage.tsx:656 | Alt+Shift+E (`exportFailedSessions`) | TODO | Pending test mapping | | | | | +| btn-desktop-66d5d9677153 | desktop | P1 | apps/desktop/src/pages/CommandTowerPage.tsx:657 | Alt+Shift+C (`() => void copyCurrentViewLink()`) | TODO | Pending test mapping | | | | | +| btn-desktop-e9f4c947fc9b | desktop | P1 | apps/desktop/src/pages/CommandTowerPage.tsx:708 | Button (`refreshNow`) | TODO | Pending test mapping | | | | | +| btn-desktop-3c36a3a681c7 | desktop | P1 | apps/desktop/src/pages/ContractsPage.tsx:25 | Refresh (`load`) | TODO | Pending test mapping | | | | | | btn-desktop-d2e26ec72426 | desktop | P1 | apps/desktop/src/pages/EventsPage.tsx:32 | Refresh (`load`) | TODO | Pending test mapping | | | | | | btn-desktop-dfe65f3bfdfe | desktop | P1 | apps/desktop/src/pages/EventsPage.tsx:49 | Button (`() => toggleExpanded(i)`) | COVERED | `apps/desktop/src/pages/EventsPage.test.tsx`(row toggle 展开/收起详情) | unit_test | apps/desktop/src/pages/EventsPage.test.tsx | unit_test | yes | -| btn-desktop-f7d23d99e52f | desktop | P1 | apps/desktop/src/pages/GodModePage.tsx:144 | Refresh (`load`) | TODO | Pending test mapping | | | | | | btn-desktop-b5a1cb9a57f9 | desktop | P1 | apps/desktop/src/pages/LocksPage.tsx:20 | Refresh (`load`) | TODO | Pending test mapping | | | | | -| btn-desktop-1fbcc9346569 | desktop | P1 | apps/desktop/src/pages/OverviewPage.tsx:163 | Refresh (`load`) | TODO | Pending test mapping | | | | | | btn-desktop-aaaae3b65369 | desktop | P1 | apps/desktop/src/pages/TestsPage.tsx:22 | Refresh (`load`) | TODO | Pending test mapping | | | | | -| btn-desktop-94a8dd2ae2df | desktop | P1 | apps/desktop/src/pages/WorkflowsPage.tsx:31 | Refresh (`load`) | TODO | Pending test mapping | | | | | +| btn-desktop-d6a1b0bbd5ba | desktop | P1 | apps/desktop/src/pages/WorkflowsPage.tsx:66 | Refresh (`load`) | TODO | Pending test mapping | | | | | | btn-desktop-2d20e437f143 | desktop | P1 | apps/desktop/src/pages/WorktreesPage.tsx:21 | Refresh (`load`) | TODO | Pending test mapping | | | | | -_generated_at: 2026-03-29T12:55:28Z_ +_generated_at: 2026-04-07T07:50:22Z_ ## Update Workflow diff --git a/docs/index.html b/docs/index.html index a583e86..5aa6622 100644 --- a/docs/index.html +++ b/docs/index.html @@ -266,39 +266,34 @@
Public landing source for CortexPilot

Command Tower for Codex and Claude Code workflows.

- CortexPilot is for teams that need more than “the agent replied.” Route Codex- and - Claude Code-style governed work through one command tower, keep every workflow case - reviewable, and attach MCP-readable proof plus replay to the same operating record. - The same front door also speaks to AI ops and platform teams that need - a shared operator view instead of scattered agent logs. + Start with one proven workflow, confirm the Workflow Case, then inspect Proof & Replay. + CortexPilot gives Codex- and Claude Code-style teams one governed operator path instead + of scattered prompts, local scripts, and after-the-fact logs.

- Current boundary: the public surface is still a repo/demo control plane, not a hosted operator product, - and the shipped MCP surface remains read-only. + Current boundary: this is a repo-backed operator control plane, not a hosted product, and the + shipped MCP surface remains read-only.

- Using Codex, Claude Code, OpenClaw, skills, or builders and not sure which door to open first? - Start with the compatibility matrix. Need release notes, the repo home, or the docs map instead? Open the - release page, the - repository, the - docs map - or - runtime topology. + After those two doors, go deeper through + agent starter kits, + read-only MCP, + integrations, + skills, + AI + MCP + API surfaces, or the + ecosystem map.

@@ -308,12 +303,30 @@

Command Tower for Codex and Claude Code workflows.

alt="CortexPilot flow from PM request to Command Tower to evidence and replay" />

- The public story should be simple: send one request, watch it move through Command Tower, - confirm the Workflow Case, then inspect Proof & Replay. + The public story should stay simple: send one request, watch it move through Command Tower, + confirm the Workflow Case, then inspect Proof & Replay before you trust the result.

+
+

Why teams take CortexPilot seriously

+
+
+

One governed first loop

+

It is not just “the model replied.” The same first loop keeps the request, case record, proof, and replay on one operator path.

+
+
+

Share-ready Workflow Cases

+

The strongest public slice is not a screenshot alone. It is the Workflow Case recap that lets other people review what happened without re-running the whole stack.

+
+
+

Truthful boundary language

+

CortexPilot explicitly says what is proven, what is still a showcase path, and what remains read-only or live-verified later.

+
+
+
+

FAQ

@@ -339,45 +352,30 @@

What should a new team open first?

Choose your path

- New here? Use this as the only decision layer on the homepage. Let the deeper pages carry the detail once you know which door fits the job. + New here? Keep the first decision simple: start with one proven workflow, choose the right adoption path, + then open deeper protocol or builder pages only after the job is clear.

-

I want the fastest proof-first run

-

Start with the public first-run cases if you want to see a governed workflow, a case record, and replayable proof before you read package internals.

-

Open use-case guide

+

I want one proven workflow first

+

Start here if you want the shortest honest story: one public baseline, one proof pack, and one share-ready Workflow Case recap before you read internals.

+

Open the first proven workflow

-

I need the compatibility matrix

-

Start here if your main question is “I use Codex, Claude Code, OpenClaw, skills, or builders. Which CortexPilot surface should I open first?”

-

Open compatibility matrix

+

I need the adoption path

+

Start here if your real question is “I use Codex, Claude Code, OpenClaw, skills, or builders. Which CortexPilot surface should I open first?”

+

Open the compatibility matrix

-

I need copy-paste client configs

-

Open the agent starter kits when your team already understands the boundary and now wants tracked Codex / Claude Code / OpenClaw setup files instead of reverse-engineering them from source.

-

Open agent starter kits

-
-
-

I need the protocol boundary

-

Start with the read-only MCP quickstart if you want the shortest truthful map of what external tools can inspect today.

-

Open read-only MCP quickstart

-
-
-

I want the agent-integration map

-

Start with the integration guide if you need the truthful answer to how Codex, Claude Code, and OpenClaw can use CortexPilot today without fake official-plugin claims.

-

Open integration guide

-
-

- Need the repo-owned playbook layer? Open the skills quickstart. Need the broader ecosystem framing? - Open the ecosystem map. Need exact Codex / Claude Code / OpenClaw starter files? Open - agent starter kits. Need the operator-layer overview? Open - AI + MCP + API surfaces. + Need exact starter files? Open agent starter kits. Need the truthful Codex / Claude Code / OpenClaw map? + Open integrations. Need repo-owned playbooks? Open skills quickstart. + Need the broader market framing? Open the ecosystem map.

@@ -398,31 +396,29 @@

Proof & Replay

- Prompt 4 now adds two ecosystem-facing layers on top of this spine: a read-only MCP server for - structured control-plane reads, and an AI operator brief that explains current run state without - creating a second truth source. -

-

- Current boundary: MCP is read-only only, and CortexPilot is not a hosted operator product today. - Write-capable MCP remains gated, and `cortexpilot.ai` should not be treated as a live hosted front door. + Read-only MCP and the AI operator brief sit on top of this spine as inspection layers, not as second execution truths.

-

First success path

+

First proven workflow

- This file is the tracked source for the live GitHub Pages landing surface. The main calls to - action intentionally point to stable public Pages and GitHub URLs so the repo, releases, and docs map stay - verifiable. The host-compatible quickstart stays: + The shortest honest start is still repo-local. The host-compatible path stays:

  1. npm run bootstrap:host
  2. CORTEXPILOT_HOST_COMPAT=1 bash scripts/test_quick.sh --no-related
  3. npm run dashboard:dev
+

+ In practice, a clean first pass should take about 5-10 minutes and end with four visible signals: + one request created from PM, one case visible in Command Tower, one Workflow Case you can confirm, + and one Proof & Replay surface you can inspect before trusting the result. +

Official first public path: start with news_digest if you want the smallest release-proven - proof loop, then use topic_brief and page_brief as public showcase expansions. + proof loop. Treat topic_brief and page_brief as showcase expansions, not equally + proven baselines.

@@ -442,8 +438,8 @@

page_brief

- See the public distribution loop in - the use-case and share-ready asset guide. + See the full proof pack, benchmark summary, and share-ready Workflow Case recap in + the first proven workflow guide.

diff --git a/docs/integrations/index.html b/docs/integrations/index.html index 3abb832..858b6ee 100644 --- a/docs/integrations/index.html +++ b/docs/integrations/index.html @@ -222,7 +222,7 @@

Codex, Claude Code, and OpenClaw integrations.

Need the fastest proof-first walkthrough instead of the deeper integration map? - Open use-case guide. + See the first proven workflow.

diff --git a/docs/releases/assets/news-digest-proof-pack-2026-03-27.json b/docs/releases/assets/news-digest-proof-pack-2026-03-27.json new file mode 100644 index 0000000..60af803 --- /dev/null +++ b/docs/releases/assets/news-digest-proof-pack-2026-03-27.json @@ -0,0 +1,35 @@ +{ + "artifact_type": "news_digest_public_proof_pack", + "task_template": "news_digest", + "status": "official_release_proven_baseline", + "baseline_scope": "single-run public baseline", + "summary": "This pack is the smallest truthful public trust bundle for CortexPilot today. It combines the official news_digest healthy proof summary, the tracked single-run benchmark summary, the share-ready Workflow Case recap, and the storefront demo-status ledger.", + "truth_boundary": { + "healthy_public_baseline": true, + "equally_proven_topic_brief": false, + "equally_proven_page_brief": false, + "healthy_live_capture_gif_present": true, + "healthy_english_first_public_capture_set_present": true, + "broader_multi_round_benchmark_present": false + }, + "primary_assets": { + "proof_summary_markdown": "docs/releases/assets/news-digest-healthy-proof-2026-03-27.md", + "proof_summary_json": "docs/releases/assets/news-digest-healthy-proof-summary-2026-03-27.json", + "benchmark_summary_markdown": "docs/releases/assets/news-digest-benchmark-summary-2026-03-27.md", + "benchmark_summary_json": "docs/releases/assets/news-digest-benchmark-summary-2026-03-27.json", + "workflow_case_recap_markdown": "docs/releases/assets/news-digest-workflow-case-recap-2026-03-27.md", + "demo_status_markdown": "docs/assets/storefront/demo-status.md" + }, + "supporting_assets": { + "gemini_proof_screenshot": "docs/releases/assets/news-digest-healthy-proof-gemini-2026-03-27.png", + "grok_proof_screenshot": "docs/releases/assets/news-digest-healthy-proof-grok-2026-03-27.png", + "dashboard_home_capture": "docs/assets/storefront/dashboard-home-live-1440x900.png", + "dashboard_command_tower_capture": "docs/assets/storefront/dashboard-command-tower-live-1440x900.png", + "dashboard_runs_capture": "docs/assets/storefront/dashboard-runs-live-1440x900.png", + "healthy_live_capture_gif": "docs/assets/storefront/dashboard-live-healthy-loop.gif" + }, + "known_gaps": [ + "No broader multi-round benchmark artifact beyond the current single-run baseline summary yet.", + "topic_brief and page_brief still need their own healthy proof and benchmark bundles." + ] +} diff --git a/docs/releases/assets/news-digest-workflow-case-recap-2026-03-27.md b/docs/releases/assets/news-digest-workflow-case-recap-2026-03-27.md index 90ce449..c564939 100644 --- a/docs/releases/assets/news-digest-workflow-case-recap-2026-03-27.md +++ b/docs/releases/assets/news-digest-workflow-case-recap-2026-03-27.md @@ -84,6 +84,5 @@ Avoid wording like: - `topic_brief` and `page_brief` remain showcase paths until they have their own healthy proof and benchmark bundles. - The missing storefront gaps still remain: - - no tracked healthy live-capture GIF - no broader multi-round public benchmark artifact - no confirmed live GitHub social preview upload diff --git a/docs/runbooks/onboarding-30min.md b/docs/runbooks/onboarding-30min.md index e54ef2b..752ab43 100644 --- a/docs/runbooks/onboarding-30min.md +++ b/docs/runbooks/onboarding-30min.md @@ -15,6 +15,7 @@ npm run bootstrap ## 2. Run the main checks ```bash +npm run ci npm run test npm run test:quick bash scripts/check_repo_hygiene.sh diff --git a/docs/runbooks/storefront-share-kit.md b/docs/runbooks/storefront-share-kit.md index 34f4039..dc1da2b 100644 --- a/docs/runbooks/storefront-share-kit.md +++ b/docs/runbooks/storefront-share-kit.md @@ -17,6 +17,12 @@ replay, read-only MCP access, and explainable operator guidance. 2. Watch it move through Command Tower. 3. Open the Workflow Case and inspect proof, replay, or the operator brief. +## Current Public Framing + +- **First proven workflow**: `news_digest` +- **Public proof pack**: healthy proof summary + benchmark summary + Workflow Case recap + demo-status ledger +- **Showcase expansions**: `topic_brief` and `page_brief` until they earn their own healthy proof bundles + ## Current Tracked Assets - `docs/assets/storefront/hero-command-tower.svg` @@ -25,6 +31,7 @@ replay, read-only MCP access, and explainable operator guidance. - `docs/assets/storefront/dashboard-home-live-1440x900.png` - `docs/assets/storefront/dashboard-command-tower-live-1440x900.png` - `docs/assets/storefront/dashboard-runs-live-1440x900.png` +- `docs/assets/storefront/dashboard-live-healthy-loop.gif` - `docs/assets/storefront/dashboard-live-degraded-loop.gif` - `docs/assets/storefront/desktop-shell-live-1440x900.png` - `docs/assets/storefront/social-preview-source.svg` @@ -34,6 +41,7 @@ replay, read-only MCP access, and explainable operator guidance. - `docs/releases/assets/news-digest-healthy-proof-gemini-2026-03-27.png` - `docs/releases/assets/news-digest-healthy-proof-grok-2026-03-27.png` - `docs/releases/assets/news-digest-benchmark-summary-2026-03-27.md` +- `docs/releases/assets/news-digest-proof-pack-2026-03-27.json` - `docs/releases/assets/news-digest-workflow-case-recap-2026-03-27.md` ## Proof Status By Asset Type @@ -41,16 +49,16 @@ replay, read-only MCP access, and explainable operator guidance. | Asset type | Current status | Safe public use | | --- | --- | --- | | Hero and storyboard art | tracked | use as explanation and storytelling | -| Local degraded dashboard captures | tracked | use only with degraded/local wording | +| Local degraded dashboard captures | tracked historical fallback | use only with degraded/local wording | | Desktop snapshot preview | tracked | use as a UI preview, not as end-to-end proof | -| Healthy backend-backed `news_digest` capture set | present | safe to reference as repo-tracked proof, not as proof of live GitHub publication | +| Healthy backend-backed dashboard capture set | present | safe to reference as repo-tracked proof, not as proof of live GitHub publication | +| Healthy backend-backed live GIF | present | safe to reference as repo-tracked proof of the local first public happy path | | Public benchmark artifact | present | safe to quote as a single-run baseline only | | Workflow Case recap asset | present | safe to reuse as the strongest public recap for the official first-run path | | GitHub Release card | present | safe to reference the live release page and the tracked draft together | ## Current Gaps -- no tracked healthy live-capture GIF yet - no broader multi-round public benchmark figure yet - no live GitHub social preview upload yet @@ -64,8 +72,9 @@ replay, read-only MCP access, and explainable operator guidance. - anchor first-look copy on the `news_digest` path when referring to a public first run - use the storyboard GIF only as a process explainer, not as proof of live runtime behavior -- describe `dashboard-home-live-1440x900.png` as a local degraded dashboard capture unless a healthy backend-backed capture replaces it -- describe `dashboard-live-degraded-loop.gif` as a local degraded dashboard capture unless a healthy backend-backed capture replaces it +- describe `dashboard-home-live-1440x900.png`, `dashboard-command-tower-live-1440x900.png`, and `dashboard-runs-live-1440x900.png` as healthy English-first local captures from the same verified path +- describe `dashboard-live-healthy-loop.gif` as the healthy backend-backed local walkthrough for the official first public baseline +- describe `dashboard-live-degraded-loop.gif` as a historical degraded dashboard capture, not the current public proof default - quote the current benchmark artifact only as a single-run `news_digest` baseline until a broader benchmark summary replaces it - if mentioning the first release, link the live GitHub Release page first and diff --git a/docs/skills/index.html b/docs/skills/index.html index 5b7b2a4..75e83fe 100644 --- a/docs/skills/index.html +++ b/docs/skills/index.html @@ -206,7 +206,7 @@

Skills quickstart for coding-agent teams.

Need the fastest proof-first walkthrough before you adopt repo-owned skills? - Open use-case guide. + See the first proven workflow.

diff --git a/docs/use-cases/index.html b/docs/use-cases/index.html index b73634b..abcd6fc 100644 --- a/docs/use-cases/index.html +++ b/docs/use-cases/index.html @@ -3,101 +3,340 @@ - CortexPilot | Public use cases and share-ready workflow assets + CortexPilot | First proven workflow and public proof pack - + - + + -
-

+

+ + +
+ The public proof-first entrypoint +

First proven workflow and public proof pack

+

+ Start here if you want the shortest honest CortexPilot story. + What can I run, inspect, and trust today before I dive into + the deeper protocol and builder surfaces? +

+

+ The answer is still: start with news_digest. It is the + only official release-proven public baseline. topic_brief and + page_brief stay useful, but they remain showcase expansions + until they each have their own healthy proof and benchmark bundles. +

+
+ +

The 10-minute proof-first loop

+
    +
  1. Start one `news_digest` run from the PM surface.
  2. +
  3. Watch it show up in Command Tower and confirm the Workflow Case exists.
  4. +
  5. Open Proof & Replay and inspect the result before trusting it.
  6. +
  7. Reuse the Workflow Case recap as the share-ready proof asset.
  8. +

If you arrived here from the agent starter kits, - this page is the post-install proof target. In plain English: after you - paste one starter file into Codex, Claude Code, or OpenClaw, come back - here and make sure the same proof-first story still holds instead of - assuming the copied config already proved a real integration. + this page is the post-install proof target. In plain English: do not + assume the starter file already proved a real integration. Come back + here and make sure the same proof pack still holds.

-

Release-proven first run

-
    -
  • news_digest: the official proof-oriented first public baseline
  • -
  • topic_brief: a bounded topic brief with search-backed evidence, currently a public showcase path
  • -
  • page_brief: a browser-backed brief over one URL, currently a public showcase path
  • -
+

What success should look like

+
+
+ Visible run +

It shows up in the product

+

One task exists in PM and one Workflow Case exists for that run. You are not relying on a shell success line alone.

+
+
+ Reviewable proof +

You can inspect what happened

+

You can open proof, replay, and recap artifacts without guessing where they went or whether the run is real.

+
+
+ Honest boundary +

You know what is proven

+

You can clearly explain why news_digest is proven today and why the other two paths are not yet equivalent.

+
+

Proof status by public case

- +
- - - + + + - - - + + - - - + + + - - - + + +
CaseCurrent proof stateWhat to inspect todayCaseCurrent proof stateWhat to inspect today
news_digestOfficial release-proven first public baseline + news_digestOfficial release-proven first public baseline healthy proof summary · single-run benchmark summary
topic_briefPublic showcase only; not yet equally release-provenTreat as discovery/story surface until a dedicated healthy proof and benchmark bundle exists.topic_briefPublic showcase only; not yet equally release-provenTreat as discovery/story surface until a dedicated healthy proof and benchmark bundle exists.
page_briefPublic showcase only; browser-backed path, not yet equally release-provenTreat as discovery/story surface until a dedicated healthy proof and benchmark bundle exists.page_briefPublic showcase only; browser-backed path, not yet equally release-provenTreat as discovery/story surface until a dedicated healthy proof and benchmark bundle exists.
-

First run to proof to share

-
    -
  1. Start one public pack
  2. -
  3. Confirm it in Command Tower, Workflow Cases, and Proof & Replay
  4. -
  5. Reuse the Workflow Case as a share-ready recap asset
  6. -
-

- For starter-kit users, the quickest truthful check today is still: - prove the repo-local path first, keep the host-tool integration - read-only, then confirm the same news_digest - proof/baseline/recap trio still makes sense for your setup. -

+

The proof pack for news_digest

+
+ This is the smallest reusable trust bundle on the public surface today: + one healthy proof summary, one single-run benchmark, one Workflow Case + recap, and one storefront status ledger that says what is still missing. +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ArtifactWhy it mattersOpen now
Healthy proof summaryShows the tracked healthy result path for the official public baseline.Open proof summary
Single-run benchmark summaryShows the current public performance baseline without pretending it is a broad benchmark campaign.Open benchmark summary
Workflow Case recapShows the strongest share-ready artifact for why Workflow Cases matter outside the operator UI.Open recap asset
Proof-pack manifestShows the machine-readable directory of what is in the public trust bundle today and what is still missing.Open proof-pack manifest
Global proof-pack indexShows the storefront-wide index of proven bundles, showcase bundles, and current public proof gaps.Open proof-pack index
Demo-status ledgerShows which storefront captures are healthy, degraded, missing, or still only explanation assets.Open demo-status ledger

Why this helps discovery

@@ -119,6 +358,9 @@

Proof files you can inspect today

  • Tracked healthy proof summary for news_digest
  • Tracked single-run benchmark baseline for news_digest
  • Tracked Workflow Case recap asset for news_digest
  • +
  • Machine-readable proof-pack manifest for news_digest
  • +
  • Global proof-pack index across public proven and showcase bundles
  • +
  • Machine-readable live-capture contract for the tracked healthy GIF and English-first public capture set
  • Demo-status ledger for healthy, degraded, and missing storefront assets
  • Benchmark methodology and wording boundary
  • @@ -127,7 +369,14 @@

    Current gaps that still matter

    • The public healthy path is strongest for news_digest; the other two cases still need their own healthy proof and benchmark bundles.
    • The current benchmark story is a tracked single-run baseline, not a broad release average.
    • -
    • The current recap story now has one tracked news_digest Workflow Case asset, but the storefront still lacks a healthy live-capture GIF and a broader benchmark artifact.
    • +
    • The current recap story now has one tracked news_digest Workflow Case asset, tracked healthy local captures and proof assets, and one remaining broader benchmark gap.
    • +
    + +

    What we still do not claim

    +
      +
    • We do not claim that topic_brief or page_brief are equally release-proven yet.
    • +
    • We do not claim that a storyboard or degraded local capture counts as healthy end-to-end proof.
    • +
    • We do not claim that the current benchmark file is a broad multi-run release average.

    diff --git a/package.json b/package.json index 38e97ec..5dd7995 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "truth:triage": "bash scripts/truth_triage.sh", "precommit:quality": "CORTEXPILOT_HOST_COMPAT=1 bash scripts/pre_commit_quality_gate.sh", "docs:render": "bash scripts/run_governance_py.sh scripts/render_docs.py", - "docs:check": "bash scripts/run_governance_py.sh scripts/check_docs_navigation_registry.py && bash scripts/run_governance_py.sh scripts/check_docs_manual_fact_boundary.py && bash scripts/run_governance_py.sh scripts/check_docs_render_freshness.py", + "docs:check": "bash scripts/run_governance_py.sh scripts/check_docs_navigation_registry.py && bash scripts/run_governance_py.sh scripts/check_docs_manual_fact_boundary.py && bash scripts/run_governance_py.sh scripts/check_frontdoor_contract.py && bash scripts/run_governance_py.sh scripts/check_storefront_proof_assets.py && bash scripts/run_governance_py.sh scripts/check_docs_render_freshness.py", "scan:secrets": "bash scripts/security_scan.sh", "scan:workflow-security": "bash scripts/check_workflow_static_security.sh", "scan:trivy": "bash scripts/check_trivy_repo_scan.sh", @@ -101,8 +101,10 @@ "bench:e2e:speed:report-only": "bash scripts/bench_e2e_speed.sh --report-only", "e2e:pm-chat": "bash scripts/e2e_pm_chat_command_tower_success.sh", "e2e:pm-chat:real": "CORTEXPILOT_E2E_RUN_MODE=real CORTEXPILOT_E2E_RUNNER=agents CORTEXPILOT_E2E_REEXEC_STRICT=true bash scripts/e2e_pm_chat_command_tower_success.sh", - "ci": "bash scripts/docker_ci.sh ci", - "ci:host": "CORTEXPILOT_HOST_COMPAT=1 bash scripts/ci.sh", + "ci": "bash scripts/ci_local_fast.sh", + "ci:host": "bash scripts/ci_local_fast.sh", + "ci:strict": "bash scripts/docker_ci.sh ci", + "ci:strict:host": "CORTEXPILOT_HOST_COMPAT=1 bash scripts/ci.sh", "ci:nightly:full": "bash scripts/ci_nightly_full.sh", "dashboard:install": "bash scripts/run_workspace_app.sh dashboard typecheck", "dashboard:dev": "bash scripts/run_workspace_app.sh dashboard dev", diff --git a/packages/frontend-shared/uiCopy.js b/packages/frontend-shared/uiCopy.js index 449f5c9..ff0c81f 100644 --- a/packages/frontend-shared/uiCopy.js +++ b/packages/frontend-shared/uiCopy.js @@ -8,7 +8,7 @@ const UI_COPY = { homePhase2: { heroTitle: "Command Tower for Codex and Claude Code workflows", heroSubtitle: - "Start one workflow case, watch Command Tower, then inspect Proof & Replay. CortexPilot keeps Codex and Claude Code work, MCP tools, evidence, and replay on one governed operator path.", + "See one proven workflow first, then choose the right adoption path. CortexPilot keeps Codex and Claude Code work, evidence, and replay on one governed operator path instead of scattered local scripts and logs.", startFirstTaskLabel: "Start first task", startNewTaskLabel: "Start new task", viewLatestRunsLabel: "View latest runs", @@ -34,11 +34,11 @@ const UI_COPY = { desc: "Inspect evidence bundles, compare reruns, and replay failures before you trust the result.", }, ], - publicTemplatesTitle: "Three public first-run cases", + publicTemplatesTitle: "One proven workflow, two showcase expansions", publicTemplatesDescription: - "Start with one public, read-only workflow case. `news_digest` is the official first public baseline; `topic_brief` and `page_brief` are showcase paths from the same front door.", - publicTemplatesActionLabel: "Open task creation", - publicTemplatesActionHref: "/pm", + "Start with `news_digest` first. It is the official public baseline. `topic_brief` and `page_brief` stay useful, but they are still showcase paths until they earn their own healthy proof bundles.", + publicTemplatesActionLabel: "Open proof pack", + publicTemplatesActionHref: "/use-cases/", publicTemplateCards: [ { href: "/pm?template=news_digest", @@ -127,8 +127,8 @@ const UI_COPY = { ], integrationTitle: "Choose the right adoption path", integrationDescription: - "Use the compatibility matrix as the front-door router, keep the use-case guide as the lighter proof-first side door, then open protocol, playbooks, packages, or AI surfaces only after the real job is clear.", - proofFirstActionLabel: "Open use-case guide", + "Use the compatibility matrix as the main router, keep the proof-first guide as the fastest way to believe the product story, then open protocol, playbooks, packages, or AI surfaces only after the real job is clear.", + proofFirstActionLabel: "See first proven workflow", proofFirstActionHref: "/use-cases/", integrationCards: [ { @@ -722,7 +722,7 @@ const UI_COPY = { homePhase2: { heroTitle: "面向 Codex 和 Claude Code 工作流的指挥塔", heroSubtitle: - "先启动一个工作流案例,再观察指挥塔,最后核对证明与回放。CortexPilot 把 Codex / Claude Code 工作、MCP 工具、证据和回放放进同一条受治理的操作路径。", + "先看一个已证明的工作流,再决定采用路径。CortexPilot 把 Codex / Claude Code 工作、证据和回放收进同一条受治理的操作路径,而不是散落在本地脚本和日志里。", startFirstTaskLabel: "启动首个任务", startNewTaskLabel: "启动新任务", viewLatestRunsLabel: "查看最近 runs", @@ -748,11 +748,11 @@ const UI_COPY = { desc: "在真正信任结果前,先检查证据包、对比重跑和失败回放。", }, ], - publicTemplatesTitle: "三个公开首跑 use case", + publicTemplatesTitle: "一个已证明工作流,两个展示扩展", publicTemplatesDescription: - "从一个公开、只读的 workflow case 开始。`news_digest` 是官方首个公开基线;`topic_brief` 和 `page_brief` 是同一前门下的展示路径。", - publicTemplatesActionLabel: "打开任务创建", - publicTemplatesActionHref: "/pm", + "先从 `news_digest` 开始。它是官方公开基线。`topic_brief` 和 `page_brief` 仍然有用,但在拿到各自健康证明包之前,它们仍属于展示路径。", + publicTemplatesActionLabel: "打开证明包", + publicTemplatesActionHref: "/use-cases/", publicTemplateCards: [ { href: "/pm?template=news_digest", @@ -841,8 +841,8 @@ const UI_COPY = { ], integrationTitle: "选择正确的采用路径", integrationDescription: - "先把 compatibility matrix 当成前门主路由,把 use-case guide 当成更轻的 proof-first 侧门,再在真正任务已经明确后进入协议、playbook、package 或 AI 页面,而不是在首页一次性读完所有说明。", - proofFirstActionLabel: "打开 use-case 指南", + "先把 compatibility matrix 当成主路由,把 proof-first 指南当成最快建立信任的入口,再在任务真正明确后进入协议、playbook、package 或 AI 页面。", + proofFirstActionLabel: "查看首个已证明工作流", proofFirstActionHref: "/use-cases/", integrationCards: [ { diff --git a/packages/frontend-shared/uiCopy.ts b/packages/frontend-shared/uiCopy.ts index 9246120..a7faec4 100644 --- a/packages/frontend-shared/uiCopy.ts +++ b/packages/frontend-shared/uiCopy.ts @@ -844,7 +844,7 @@ const UI_COPY: Record = { homePhase2: { heroTitle: "Command Tower for Codex and Claude Code workflows", heroSubtitle: - "Start one workflow case, watch Command Tower, then inspect Proof & Replay. CortexPilot keeps Codex and Claude Code work, MCP tools, evidence, and replay on one governed operator path.", + "See one proven workflow first, then choose the right adoption path. CortexPilot keeps Codex and Claude Code work, evidence, and replay on one governed operator path instead of scattered local scripts and logs.", startFirstTaskLabel: "Start first task", startNewTaskLabel: "Start new task", viewLatestRunsLabel: "View latest runs", @@ -870,11 +870,11 @@ const UI_COPY: Record = { desc: "Inspect evidence bundles, compare reruns, and replay failures before you trust the result.", }, ], - publicTemplatesTitle: "Three public first-run cases", + publicTemplatesTitle: "One proven workflow, two showcase expansions", publicTemplatesDescription: - "Start with one public, read-only workflow case. `news_digest` is the official first public baseline; `topic_brief` and `page_brief` are showcase paths from the same front door.", - publicTemplatesActionLabel: "Open task creation", - publicTemplatesActionHref: "/pm", + "Start with `news_digest` first. It is the official public baseline. `topic_brief` and `page_brief` stay useful, but they are still showcase paths until they earn their own healthy proof bundles.", + publicTemplatesActionLabel: "Open proof pack", + publicTemplatesActionHref: "/use-cases/", publicTemplateCards: [ { href: "/pm?template=news_digest", @@ -963,8 +963,8 @@ const UI_COPY: Record = { ], integrationTitle: "Choose the right adoption path", integrationDescription: - "Use the compatibility matrix as the front-door router, keep the use-case guide as the lighter proof-first side door, then open protocol, playbooks, packages, or AI surfaces only after the real job is clear.", - proofFirstActionLabel: "Open use-case guide", + "Use the compatibility matrix as the main router, keep the proof-first guide as the fastest way to believe the product story, then open protocol, playbooks, packages, or AI surfaces only after the real job is clear.", + proofFirstActionLabel: "See first proven workflow", proofFirstActionHref: "/use-cases/", integrationCards: [ { @@ -1896,7 +1896,7 @@ const UI_COPY: Record = { homePhase2: { heroTitle: "面向 Codex 和 Claude Code 工作流的指挥塔", heroSubtitle: - "先启动一个工作流案例,再观察指挥塔,最后核对证明与回放。CortexPilot 把 Codex / Claude Code 工作、MCP 工具、证据和回放放进同一条受治理的操作路径。", + "先看一个已证明的工作流,再决定采用路径。CortexPilot 把 Codex / Claude Code 工作、证据和回放收进同一条受治理的操作路径,而不是散落在本地脚本和日志里。", startFirstTaskLabel: "启动首个任务", startNewTaskLabel: "启动新任务", viewLatestRunsLabel: "查看最近 runs", @@ -1922,11 +1922,11 @@ const UI_COPY: Record = { desc: "在真正信任结果前,先检查证据包、对比重跑和失败回放。", }, ], - publicTemplatesTitle: "三个公开首跑 use case", + publicTemplatesTitle: "一个已证明工作流,两个展示扩展", publicTemplatesDescription: - "从一个公开、只读的 workflow case 开始。`news_digest` 是官方首个公开基线;`topic_brief` 和 `page_brief` 是同一前门下的展示路径。", - publicTemplatesActionLabel: "打开任务创建", - publicTemplatesActionHref: "/pm", + "先从 `news_digest` 开始。它是官方公开基线。`topic_brief` 和 `page_brief` 仍然有用,但在拿到各自健康证明包之前,它们仍属于展示路径。", + publicTemplatesActionLabel: "打开证明包", + publicTemplatesActionHref: "/use-cases/", publicTemplateCards: [ { href: "/pm?template=news_digest", @@ -2015,8 +2015,8 @@ const UI_COPY: Record = { ], integrationTitle: "选择正确的采用路径", integrationDescription: - "先把 compatibility matrix 当成前门主路由,把 use-case guide 当成更轻的 proof-first 侧门,再在真正任务已经明确后进入协议、playbook、package 或 AI 页面,而不是在首页一次性读完所有说明。", - proofFirstActionLabel: "打开 use-case 指南", + "先把 compatibility matrix 当成主路由,把 proof-first 指南当成最快建立信任的入口,再在任务真正明确后进入协议、playbook、package 或 AI 页面。", + proofFirstActionLabel: "查看首个已证明工作流", proofFirstActionHref: "/use-cases/", integrationCards: [ { diff --git a/scripts/check_docs_render_freshness.py b/scripts/check_docs_render_freshness.py index 7fb6ffc..a08386f 100644 --- a/scripts/check_docs_render_freshness.py +++ b/scripts/check_docs_render_freshness.py @@ -65,6 +65,9 @@ def main() -> int: if not output.exists(): errors.append(f"manifest output missing: {output_rel}") continue + freshness_strategy = str(item.get("freshness_strategy") or "timestamp").strip().lower() + if freshness_strategy == "existence_only": + continue output_mtime = output.stat().st_mtime for source_rel in item.get("source_inputs") or []: source = ROOT / str(source_rel) diff --git a/scripts/check_frontdoor_contract.py b/scripts/check_frontdoor_contract.py new file mode 100644 index 0000000..bd66fd6 --- /dev/null +++ b/scripts/check_frontdoor_contract.py @@ -0,0 +1,239 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +from html.parser import HTMLParser +from pathlib import Path +import re + + +ROOT = Path(__file__).resolve().parents[1] +INDEX_PATH = ROOT / "docs" / "index.html" +USE_CASES_PATH = ROOT / "docs" / "use-cases" / "index.html" +COMPATIBILITY_PATH = ROOT / "docs" / "compatibility" / "index.html" +PROOF_SUMMARY_PATH = ROOT / "docs" / "releases" / "assets" / "news-digest-healthy-proof-2026-03-27.md" +BENCHMARK_SUMMARY_PATH = ROOT / "docs" / "releases" / "assets" / "news-digest-benchmark-summary-2026-03-27.md" +WORKFLOW_RECAP_PATH = ROOT / "docs" / "releases" / "assets" / "news-digest-workflow-case-recap-2026-03-27.md" +PROOF_PACK_MANIFEST_PATH = ROOT / "docs" / "releases" / "assets" / "news-digest-proof-pack-2026-03-27.json" +PROOF_PACK_INDEX_PATH = ROOT / "docs" / "assets" / "storefront" / "proof-pack-index.json" +DEMO_STATUS_PATH = ROOT / "docs" / "assets" / "storefront" / "demo-status.md" + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="Validate the static public front-door contract for CortexPilot." + ) + return parser.parse_args() + + +def _normalize_text(value: str) -> str: + return re.sub(r"\s+", " ", value).strip() + + +class _AnchorParser(HTMLParser): + def __init__(self) -> None: + super().__init__() + self.anchors: list[tuple[str, str]] = [] + self._current_href: str | None = None + self._parts: list[str] = [] + + def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None: + if tag.lower() != "a": + return + href = dict(attrs).get("href") + if href: + self._current_href = href + self._parts = [] + + def handle_data(self, data: str) -> None: + if self._current_href is not None: + self._parts.append(data) + + def handle_endtag(self, tag: str) -> None: + if tag.lower() != "a" or self._current_href is None: + return + text = _normalize_text("".join(self._parts)) + self.anchors.append((self._current_href, text)) + self._current_href = None + self._parts = [] + + +def _read_html(path: Path) -> str: + if not path.exists(): + raise FileNotFoundError(path) + return path.read_text(encoding="utf-8") + + +def _parse_anchors(path: Path) -> list[tuple[str, str]]: + parser = _AnchorParser() + parser.feed(_read_html(path)) + return parser.anchors + + +def _require_substrings(path: Path, content: str, required: list[str], errors: list[str]) -> None: + normalized = _normalize_text(content) + for snippet in required: + if _normalize_text(snippet) not in normalized: + errors.append(f"{path.relative_to(ROOT)} missing required text: {snippet}") + + +def _require_anchor(path: Path, anchors: list[tuple[str, str]], href: str, text: str, errors: list[str]) -> None: + target = (_normalize_text(href), _normalize_text(text)) + normalized = [(_normalize_text(item_href), _normalize_text(item_text)) for item_href, item_text in anchors] + if target not in normalized: + errors.append( + f"{path.relative_to(ROOT)} missing required link: text='{text}' href='{href}'" + ) + + +def main() -> int: + _ = parse_args() + errors: list[str] = [] + + required_paths = [ + INDEX_PATH, + USE_CASES_PATH, + COMPATIBILITY_PATH, + PROOF_SUMMARY_PATH, + BENCHMARK_SUMMARY_PATH, + WORKFLOW_RECAP_PATH, + PROOF_PACK_MANIFEST_PATH, + PROOF_PACK_INDEX_PATH, + DEMO_STATUS_PATH, + ] + for path in required_paths: + if not path.exists(): + errors.append(f"required front-door artifact missing: {path.relative_to(ROOT)}") + + if errors: + print("❌ [frontdoor-contract] missing required artifacts:") + for item in errors: + print(f"- {item}") + return 1 + + index_html = _read_html(INDEX_PATH) + use_cases_html = _read_html(USE_CASES_PATH) + compatibility_html = _read_html(COMPATIBILITY_PATH) + + _require_substrings( + INDEX_PATH, + index_html, + [ + "See the first proven workflow", + "Choose the right adoption path", + "repo-backed operator control plane, not a hosted product", + "shipped MCP surface remains read-only", + "news_digest", + "topic_brief", + "page_brief", + ], + errors, + ) + + _require_substrings( + USE_CASES_PATH, + use_cases_html, + [ + "First proven workflow and public proof pack", + "news_digest", + "only official release-proven public baseline", + "topic_brief", + "page_brief", + "not yet equally release-proven", + "What we still do not claim", + ], + errors, + ) + + _require_substrings( + COMPATIBILITY_PATH, + compatibility_html, + [ + "One truthful compatibility matrix for modern coding-agent teams.", + "read-only MCP", + "See the first proven workflow", + ], + errors, + ) + + use_case_anchors = _parse_anchors(USE_CASES_PATH) + _require_anchor( + USE_CASES_PATH, + use_case_anchors, + "../releases/assets/news-digest-healthy-proof-2026-03-27.md", + "Open proof summary", + errors, + ) + _require_anchor( + USE_CASES_PATH, + use_case_anchors, + "../releases/assets/news-digest-benchmark-summary-2026-03-27.md", + "Open benchmark summary", + errors, + ) + _require_anchor( + USE_CASES_PATH, + use_case_anchors, + "../releases/assets/news-digest-workflow-case-recap-2026-03-27.md", + "Open recap asset", + errors, + ) + _require_anchor( + USE_CASES_PATH, + use_case_anchors, + "../releases/assets/news-digest-proof-pack-2026-03-27.json", + "Open proof-pack manifest", + errors, + ) + _require_anchor( + USE_CASES_PATH, + use_case_anchors, + "../assets/storefront/proof-pack-index.json", + "Open proof-pack index", + errors, + ) + _require_anchor( + USE_CASES_PATH, + use_case_anchors, + "../assets/storefront/demo-status.md", + "Open demo-status ledger", + errors, + ) + + index_anchors = _parse_anchors(INDEX_PATH) + _require_anchor( + INDEX_PATH, + index_anchors, + "./use-cases/", + "See the first proven workflow", + errors, + ) + _require_anchor( + INDEX_PATH, + index_anchors, + "./compatibility/", + "Choose the right adoption path", + errors, + ) + + compatibility_anchors = _parse_anchors(COMPATIBILITY_PATH) + _require_anchor( + COMPATIBILITY_PATH, + compatibility_anchors, + "../use-cases/", + "See the first proven workflow", + errors, + ) + + if errors: + print("❌ [frontdoor-contract] public front-door contract violations:") + for item in errors: + print(f"- {item}") + return 1 + + print("✅ [frontdoor-contract] public front-door contract satisfied") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/check_github_control_plane.py b/scripts/check_github_control_plane.py index 4fc2bd9..2d165e7 100644 --- a/scripts/check_github_control_plane.py +++ b/scripts/check_github_control_plane.py @@ -32,6 +32,16 @@ def _repo_path_exists(relative_path: str) -> bool: return (ROOT / relative_path).exists() +def _security_feature_status(repo_payload: dict, feature_name: str) -> str: + security = repo_payload.get("security_and_analysis") + if not isinstance(security, dict): + return "" + feature = security.get(feature_name) + if not isinstance(feature, dict): + return "" + return str(feature.get("status") or "").strip() + + def main() -> int: parser = argparse.ArgumentParser(description="Validate live GitHub control-plane settings against repo policy.") parser.add_argument("--policy", default=str(DEFAULT_POLICY)) @@ -102,6 +112,19 @@ def main() -> int: vulnerability_alerts_required = bool((platform_evidence.get("vulnerability_alerts") or {}).get("required")) if vulnerability_alerts_required and vuln_alerts_code != 0: errors.append(f"vulnerability alerts not proven: {vuln_alerts_payload}") + for feature_name in ( + "secret_scanning", + "secret_scanning_push_protection", + "secret_scanning_non_provider_patterns", + "secret_scanning_validity_checks", + ): + feature_rule = platform_evidence.get(feature_name) if isinstance(platform_evidence.get(feature_name), dict) else {} + if feature_rule.get("required"): + status = _security_feature_status(repo_payload, feature_name) + if status != "enabled": + errors.append( + f"{feature_name} drift: actual={status or 'missing'!r} expected='enabled'" + ) dependabot_rule = platform_evidence.get("dependabot_config") if isinstance(platform_evidence.get("dependabot_config"), dict) else {} dependabot_path = str(dependabot_rule.get("path") or "").strip() if dependabot_path and not _repo_path_exists(dependabot_path): @@ -137,6 +160,7 @@ def main() -> int: "environments": env_payload if env_code == 0 else {"error": env_payload}, "private_vulnerability_reporting": pvr_payload if pvr_code == 0 else {"error": pvr_payload}, "vulnerability_alerts": {"enabled": True} if vuln_alerts_code == 0 else {"error": vuln_alerts_payload}, + "security_and_analysis": repo_payload.get("security_and_analysis") if repo_code == 0 else {"error": repo_payload}, "codeql_default_setup": codeql_payload if codeql_code == 0 else {"error": codeql_payload}, "dependabot_alerts": dependabot_payload if dependabot_code == 0 else {"error": dependabot_payload}, "errors": errors, diff --git a/scripts/check_storefront_proof_assets.py b/scripts/check_storefront_proof_assets.py new file mode 100644 index 0000000..eee67cf --- /dev/null +++ b/scripts/check_storefront_proof_assets.py @@ -0,0 +1,260 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import importlib.util +import json +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +PROOF_PACK_INDEX = ROOT / "docs" / "assets" / "storefront" / "proof-pack-index.json" +DEMO_STATUS_PATH = ROOT / "docs" / "assets" / "storefront" / "demo-status.md" +LIVE_CAPTURE_REQUIREMENTS_PATH = ROOT / "docs" / "assets" / "storefront" / "live-capture-requirements.json" +SHARE_KIT_PATH = ROOT / "docs" / "runbooks" / "storefront-share-kit.md" +USE_CASES_PATH = ROOT / "docs" / "use-cases" / "index.html" + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="Validate the public storefront proof asset contract." + ) + return parser.parse_args() + + +def _load_json(path: Path) -> dict: + payload = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(payload, dict): + raise ValueError(f"{path} must contain a JSON object") + return payload + + +def _require(condition: bool, message: str, errors: list[str]) -> None: + if not condition: + errors.append(message) + + +def _asset_exists(path_text: str, errors: list[str], *, reason: str) -> None: + path = ROOT / path_text + if not path.exists(): + errors.append(f"{reason}: missing asset {path_text}") + + +def _require_text(path: Path, snippets: list[str], errors: list[str]) -> None: + text = path.read_text(encoding="utf-8") + for snippet in snippets: + if snippet not in text: + errors.append(f"{path.relative_to(ROOT)} missing required text: {snippet}") + + +def _load_generator_module() -> object: + script_path = Path(__file__).resolve().with_name("generate_storefront_proof_pack_index.py") + spec = importlib.util.spec_from_file_location("cortexpilot_generate_storefront_proof_pack_index", script_path) + module = importlib.util.module_from_spec(spec) + assert spec and spec.loader + spec.loader.exec_module(module) + return module + + +def main() -> int: + _ = parse_args() + errors: list[str] = [] + + if not PROOF_PACK_INDEX.exists(): + print("❌ [storefront-proof-assets] proof-pack index missing") + return 1 + if not LIVE_CAPTURE_REQUIREMENTS_PATH.exists(): + print("❌ [storefront-proof-assets] live capture requirements missing") + return 1 + + generator = _load_generator_module() + generator.ROOT = ROOT + generator.REGISTRY_PATH = ROOT / "configs" / "storefront_proof_bundle_registry.json" + generator.OUTPUT_PATH = PROOF_PACK_INDEX + registry_payload = generator._load_json(generator.REGISTRY_PATH) + registry_payload["source_registry"] = generator.REGISTRY_PATH.relative_to(ROOT).as_posix() + expected_payload = generator.build_index(registry_payload) + current_payload = _load_json(PROOF_PACK_INDEX) + if current_payload != expected_payload: + errors.append("proof-pack index drifted from generator output") + + payload = current_payload + live_capture_requirements = _load_json(LIVE_CAPTURE_REQUIREMENTS_PATH) + _require( + payload.get("artifact_type") == "cortexpilot_public_proof_pack_index", + "proof-pack index has unexpected artifact_type", + errors, + ) + _require( + live_capture_requirements.get("artifact_type") == "cortexpilot_storefront_live_capture_requirements", + "live capture requirements has unexpected artifact_type", + errors, + ) + + vocabulary = payload.get("vocabulary_contract") + _require(isinstance(vocabulary, dict), "proof-pack index missing vocabulary_contract", errors) + if isinstance(vocabulary, dict): + _require( + vocabulary.get("proven_workflow_label") == "first proven workflow", + "proof-pack index must pin the proven workflow label", + errors, + ) + _require( + vocabulary.get("proof_pack_label") == "public proof pack", + "proof-pack index must pin the proof pack label", + errors, + ) + + bundles = payload.get("bundles") + _require(isinstance(bundles, list), "proof-pack index missing bundles[]", errors) + bundle_map = {} + if isinstance(bundles, list): + for item in bundles: + if isinstance(item, dict) and isinstance(item.get("bundle_id"), str): + bundle_map[item["bundle_id"]] = item + + for bundle_id in ("news_digest", "topic_brief", "page_brief"): + _require(bundle_id in bundle_map, f"missing proof bundle `{bundle_id}`", errors) + + news = bundle_map.get("news_digest", {}) + if isinstance(news, dict): + _require(news.get("proof_state") == "release_proven", "news_digest must stay release_proven", errors) + _require( + news.get("claim_scope") == "official_first_public_baseline", + "news_digest must stay the official first public baseline", + errors, + ) + pack_manifest = str(news.get("pack_manifest") or "").strip() + _require(bool(pack_manifest), "news_digest must reference a pack_manifest", errors) + if pack_manifest: + _asset_exists(pack_manifest, errors, reason="news_digest pack manifest") + + capture_contract = news.get("capture_contract") + _require(isinstance(capture_contract, dict), "news_digest missing capture_contract", errors) + if isinstance(capture_contract, dict): + _require( + capture_contract.get("healthy_live_capture_gif_present") is True, + "news_digest capture contract must acknowledge the landed healthy live-capture GIF", + errors, + ) + _require( + capture_contract.get("healthy_english_first_public_capture_set_present") is True, + "news_digest capture contract must acknowledge the landed healthy English-first public capture set", + errors, + ) + + missing = news.get("missing_expected_artifacts") + _require(isinstance(missing, list), "news_digest missing expected_artifacts list", errors) + if isinstance(missing, list): + required_missing = {"broader_multi_round_benchmark"} + missing_set = {str(item) for item in missing} + if not required_missing.issubset(missing_set): + errors.append("news_digest missing_expected_artifacts must retain the broader benchmark gap") + forbidden_missing = { + "healthy_live_capture_gif", + "healthy_english_first_public_capture_set", + } + if forbidden_missing & missing_set: + errors.append("news_digest missing_expected_artifacts still lists landed healthy capture assets") + + assets = news.get("assets") + _require(isinstance(assets, list), "news_digest missing assets[]", errors) + if isinstance(assets, list): + roles = {str(item.get("role")) for item in assets if isinstance(item, dict)} + required_roles = { + "healthy_proof_summary", + "healthy_proof_summary_machine", + "benchmark_summary", + "benchmark_summary_machine", + "workflow_case_recap", + "demo_status_ledger", + "dashboard_home_capture", + "dashboard_command_tower_capture", + "dashboard_runs_capture", + "healthy_live_capture_gif", + } + if not required_roles.issubset(roles): + errors.append("news_digest bundle lost one or more required proof asset roles") + for item in assets: + if not isinstance(item, dict): + errors.append("news_digest assets[] must contain objects") + continue + path_text = str(item.get("path") or "").strip() + if not path_text: + errors.append("news_digest assets[] contains an entry without path") + continue + _asset_exists(path_text, errors, reason="news_digest asset") + + for showcase_id in ("topic_brief", "page_brief"): + bundle = bundle_map.get(showcase_id, {}) + if isinstance(bundle, dict): + _require( + bundle.get("proof_state") == "showcase_only", + f"{showcase_id} must stay showcase_only until its own healthy proof bundle exists", + errors, + ) + missing = bundle.get("missing_expected_artifacts") + _require( + isinstance(missing, list) and len(missing) > 0, + f"{showcase_id} must keep explicit missing_expected_artifacts", + errors, + ) + + _require_text( + DEMO_STATUS_PATH, + [ + "Healthy backend-backed dashboard capture set", + "Healthy backend-backed live GIF", + "safe repo-side proof of a healthy local first public path", + ], + errors, + ) + _require_text( + SHARE_KIT_PATH, + [ + "Healthy backend-backed dashboard capture set", + "Healthy backend-backed live GIF", + "safe to reference as repo-tracked proof, not as proof of live GitHub publication", + ], + errors, + ) + _require_text( + USE_CASES_PATH, + [ + "tracked healthy local captures and proof assets", + "The current benchmark story is a tracked single-run baseline, not a broad release average.", + "Global proof-pack index across public proven and showcase bundles", + ], + errors, + ) + requirements_assets = live_capture_requirements.get("required_assets") + _require(isinstance(requirements_assets, list), "live capture requirements missing required_assets[]", errors) + if isinstance(requirements_assets, list): + required_ids = { + "healthy_live_capture_gif", + "healthy_english_first_dashboard_home_capture", + "healthy_english_first_command_tower_capture", + "healthy_english_first_runs_capture", + } + seen_ids = {str(item.get("asset_id")) for item in requirements_assets if isinstance(item, dict)} + if not required_ids.issubset(seen_ids): + errors.append("live capture requirements lost one or more required asset ids") + for item in requirements_assets: + if not isinstance(item, dict): + errors.append("live capture requirements entries must be objects") + continue + if str(item.get("status") or "").strip() != "present": + errors.append("live capture requirements must mark landed assets as present") + + if errors: + print("❌ [storefront-proof-assets] public proof asset contract violations:") + for item in errors: + print(f"- {item}") + return 1 + + print("✅ [storefront-proof-assets] public proof asset contract satisfied") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/ci_local_fast.sh b/scripts/ci_local_fast.sh new file mode 100644 index 0000000..d29d44f --- /dev/null +++ b/scripts/ci_local_fast.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$ROOT_DIR" + +source "$ROOT_DIR/scripts/lib/toolchain_env.sh" + +export PYTHONDONTWRITEBYTECODE=1 +export CORTEXPILOT_HOST_COMPAT=1 + +RUNNER_TEMP_DIR="${RUNNER_TEMP:-$ROOT_DIR/.runtime-cache/cache/tmp/runner}" +mkdir -p "$RUNNER_TEMP_DIR" +export RUNNER_TEMP="$RUNNER_TEMP_DIR" + +echo "🚦 [ci-local-fast] start hosted-aligned local fast gate" + +# Keep the default local CI path lightweight and deterministic. +# Full strict CI remains available via npm run ci:strict. +CORTEXPILOT_DOCTOR_REQUIRE_DOCKER=0 \ +CORTEXPILOT_DOCTOR_REQUIRE_SUDO=0 \ +bash scripts/ci_control_plane_doctor.sh + +bash scripts/test_ci_policy_resolution.sh +bash scripts/test_perf_smoke_policy_resolution.sh +bash scripts/check_workflow_static_security.sh +bash scripts/test_quick.sh --no-related + +echo "✅ [ci-local-fast] completed" diff --git a/scripts/generate_storefront_proof_pack_index.py b/scripts/generate_storefront_proof_pack_index.py new file mode 100644 index 0000000..91a5828 --- /dev/null +++ b/scripts/generate_storefront_proof_pack_index.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import json +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[1] +REGISTRY_PATH = ROOT / "configs" / "storefront_proof_bundle_registry.json" +OUTPUT_PATH = ROOT / "docs" / "assets" / "storefront" / "proof-pack-index.json" + +PRIMARY_ROLE_MAP: dict[str, dict[str, Any]] = { + "proof_summary_markdown": { + "role": "healthy_proof_summary", + "format": "markdown", + "truth_class": "repo_side_proof", + "required_for_claim": True, + }, + "proof_summary_json": { + "role": "healthy_proof_summary_machine", + "format": "json", + "truth_class": "repo_side_machine_summary", + "required_for_claim": True, + }, + "benchmark_summary_markdown": { + "role": "benchmark_summary", + "format": "markdown", + "truth_class": "repo_side_benchmark", + "required_for_claim": True, + }, + "benchmark_summary_json": { + "role": "benchmark_summary_machine", + "format": "json", + "truth_class": "repo_side_machine_summary", + "required_for_claim": True, + }, + "workflow_case_recap_markdown": { + "role": "workflow_case_recap", + "format": "markdown", + "truth_class": "share_ready_recap", + "required_for_claim": True, + }, + "demo_status_markdown": { + "role": "demo_status_ledger", + "format": "markdown", + "truth_class": "truth_boundary_ledger", + "required_for_claim": True, + }, +} + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Generate the public storefront proof-pack index.") + parser.add_argument("--registry", default=str(REGISTRY_PATH)) + parser.add_argument("--output", default=str(OUTPUT_PATH)) + parser.add_argument("--check", action="store_true") + return parser.parse_args() + + +def _load_json(path: Path) -> dict[str, Any]: + payload = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(payload, dict): + raise SystemExit(f"❌ [generate-storefront-proof-pack-index] expected JSON object: {path}") + return payload + + +def _normalize_asset(path_text: str, descriptor: dict[str, Any]) -> dict[str, Any]: + item = {"path": path_text} + item.update(descriptor) + return item + + +def _supporting_asset(path_text: str, key: str) -> dict[str, Any]: + ext = Path(path_text).suffix.lower().lstrip(".") or "unknown" + return { + "path": path_text, + "role": key, + "format": ext, + "truth_class": "supporting_capture", + "required_for_claim": False, + } + + +def build_index(registry_payload: dict[str, Any]) -> dict[str, Any]: + bundles_payload = registry_payload.get("bundles") + if not isinstance(bundles_payload, list): + raise SystemExit("❌ [generate-storefront-proof-pack-index] registry missing bundles[]") + + rendered_bundles: list[dict[str, Any]] = [] + for bundle in bundles_payload: + if not isinstance(bundle, dict): + raise SystemExit("❌ [generate-storefront-proof-pack-index] bundle entries must be objects") + rendered = dict(bundle) + assets: list[dict[str, Any]] = [] + pack_manifest_rel = str(bundle.get("pack_manifest") or "").strip() + if pack_manifest_rel: + pack_manifest = _load_json(ROOT / pack_manifest_rel) + rendered.setdefault("safe_public_claims", bundle.get("safe_public_claims", [])) + rendered.setdefault("forbidden_claims", bundle.get("forbidden_claims", [])) + rendered.setdefault("missing_expected_artifacts", bundle.get("missing_expected_artifacts", [])) + + primary_assets = pack_manifest.get("primary_assets") + if isinstance(primary_assets, dict): + for key, descriptor in PRIMARY_ROLE_MAP.items(): + path_text = str(primary_assets.get(key) or "").strip() + if path_text: + assets.append(_normalize_asset(path_text, descriptor)) + + supporting_assets = pack_manifest.get("supporting_assets") + if isinstance(supporting_assets, dict): + for key, path_value in supporting_assets.items(): + path_text = str(path_value or "").strip() + if path_text: + assets.append(_supporting_asset(path_text, key)) + + rendered["pack_manifest"] = pack_manifest_rel + + rendered["assets"] = assets + rendered_bundles.append(rendered) + + return { + "artifact_type": "cortexpilot_public_proof_pack_index", + "generated_by": "scripts/generate_storefront_proof_pack_index.py", + "source_registry": str(Path(registry_payload.get("source_registry") or "configs/storefront_proof_bundle_registry.json")), + "vocabulary_contract": registry_payload.get("vocabulary_contract", {}), + "bundles": rendered_bundles, + } + + +def main() -> int: + args = parse_args() + registry_path = Path(args.registry).expanduser().resolve() + output_path = Path(args.output).expanduser().resolve() + + registry_payload = _load_json(registry_path) + registry_payload["source_registry"] = registry_path.relative_to(ROOT).as_posix() + rendered = build_index(registry_payload) + rendered_json = json.dumps(rendered, ensure_ascii=False, indent=2) + "\n" + + if args.check: + if not output_path.exists(): + print(f"❌ [generate-storefront-proof-pack-index] missing output: {output_path.relative_to(ROOT)}") + return 1 + current = output_path.read_text(encoding="utf-8") + if current != rendered_json: + print("❌ [generate-storefront-proof-pack-index] output drift detected") + return 1 + print("✅ [generate-storefront-proof-pack-index] output is up to date") + return 0 + + output_path.parent.mkdir(parents=True, exist_ok=True) + output_path.write_text(rendered_json, encoding="utf-8") + print(f"✅ [generate-storefront-proof-pack-index] wrote {output_path.relative_to(ROOT)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/pre_commit_quality_gate.sh b/scripts/pre_commit_quality_gate.sh index 97ad0b6..336886f 100755 --- a/scripts/pre_commit_quality_gate.sh +++ b/scripts/pre_commit_quality_gate.sh @@ -66,7 +66,7 @@ run_gate() { GATE_LOGS+=("$log_file") } -echo "🚦 [pre-commit-quality-gate] scope=$scope start parallel core quality gates" +echo "🚦 [pre-commit-quality-gate] scope=$scope start fast local commit gates" run_gate "lint" bash scripts/pre_commit_lint_gate.sh run_gate "governance_python_entrypoints" bash scripts/check_governance_python_entrypoints.sh @@ -76,9 +76,6 @@ run_gate "actionlint" bash scripts/check_actionlint.sh run_gate "zizmor" bash scripts/check_zizmor.sh --offline "$ROOT_DIR" run_gate "docs_navigation_registry" bash scripts/run_governance_py.sh scripts/check_docs_navigation_registry.py run_gate "docs_fact_boundary" bash scripts/run_governance_py.sh scripts/check_docs_manual_fact_boundary.py -run_gate "github_security_alerts" bash scripts/run_governance_py.sh scripts/check_github_security_alerts.py --mode require --repo xiaojiou176-open/CortexPilot-public -run_gate "doc_drift" bash scripts/hooks/doc_drift_gate.sh -run_gate "doc_sync" bash scripts/hooks/doc_sync_gate.sh if [[ "$run_test_smell" -eq 1 ]]; then run_gate "test_smell" bash scripts/test_smell_gate.sh else diff --git a/scripts/pre_push_quality_gate.sh b/scripts/pre_push_quality_gate.sh index ce2963c..a52cd3c 100755 --- a/scripts/pre_push_quality_gate.sh +++ b/scripts/pre_push_quality_gate.sh @@ -135,8 +135,9 @@ require_skip_precommit_break_glass_or_fail() { echo "🚦 [pre-push-quality-gate] local-first layered gate start" # Layered gate strategy: -# - Pre-commit handles: lint, doc_drift, doc_sync, test_smell (incremental) -# - Pre-push handles: env governance, contract checks, incremental tests, external probe +# - Pre-commit handles: cheap local commit gates + incremental test_smell +# - Pre-push default handles: light repo contracts + quick tests +# - Pre-push strict bundle handles: scanners, reports, external probe, broader local CI mirror # - CI handles: full comprehensive checks (catch-all for --no-verify bypass) # Check if pre-commit already passed recently (within 5 minutes) @@ -159,44 +160,42 @@ else fi if [[ "$skip_lint_doc_gates" != "1" ]]; then - echo "🔍 [pre-push-quality-gate] running lint and doc gates (pre-commit not detected)" + echo "🔍 [pre-push-quality-gate] running lint gate (pre-commit not detected)" bash scripts/pre_commit_lint_gate.sh - bash scripts/hooks/doc_drift_gate.sh - bash scripts/hooks/doc_sync_gate.sh else - echo "⏭️ [pre-push-quality-gate] skipped lint/doc gates (already passed in pre-commit)" + echo "⏭️ [pre-push-quality-gate] skipped duplicate lint gate (already passed in pre-commit)" fi -# Pre-push exclusive gates (not in pre-commit) -echo "🔍 [pre-push-quality-gate] running pre-push exclusive gates" +# Pre-push fast-path gates (not in pre-commit) +echo "🔍 [pre-push-quality-gate] running pre-push fast-path gates" bash scripts/check_governance_python_entrypoints.sh bash scripts/check_workflow_static_security.sh -bash scripts/check_secret_scan_closeout.sh --mode current -bash scripts/check_trivy_repo_scan.sh bash scripts/run_governance_py.sh scripts/check_repo_positioning.py bash scripts/run_governance_py.sh scripts/check_relocation_residues.py -bash scripts/run_governance_py.sh scripts/check_github_security_alerts.py --mode require --repo xiaojiou176-open/CortexPilot-public -bash scripts/run_governance_py.sh scripts/check_developer_facing_english.py -bash scripts/run_governance_py.sh scripts/check_third_party_asset_registry.py -bash scripts/run_governance_py.sh scripts/check_root_semantic_cleanliness.py bash scripts/run_governance_py.sh scripts/check_env_governance.py --mode gate --max-deprecated-count 10 --max-deprecated-ratio 0.03 -bash scripts/run_governance_py.sh scripts/refresh_governance_evidence_manifest.py -bash scripts/run_governance_py.sh scripts/build_governance_scorecard.py --enforce -bash scripts/run_governance_py.sh scripts/build_governance_closeout_report.py --mode pre-push -bash scripts/run_governance_py.sh scripts/check_active_report_identity.py -bash scripts/run_governance_py.sh scripts/check_workflow_runner_governance.py -bash scripts/run_governance_py.sh scripts/check_docs_render_freshness.py bash scripts/run_governance_py.sh scripts/check_changed_scope_map.py bash scripts/run_governance_py.sh scripts/check_e2e_marker_consistency.py echo "ℹ️ [pre-push-quality-gate] skip desktop Cargo.lock audit in the default path; Linux/BSD desktop native graph review stays manual-only via bash scripts/docker_ci.sh lane desktop-native-smoke, and excluded unsupported-surface advisories must remain declared in configs/cargo_audit_ignored_advisories.json + governance closeout." # Local-first layered rule: -# pre-push runs a strict local CI profile (heavier than pre-commit, lighter than remote strict CI), -# and remote CI remains the highest-strictness second-pass verifier. -# Break-glass escape is explicit + auditable via reason/ticket. -run_local_ci="${CORTEXPILOT_PRE_PUSH_RUN_CI_DOUBLE_CHECK:-1}" +# pre-push runs a lightweight fast path by default and keeps the old strict +# local mirror as an explicit opt-in. Remote CI remains the highest-strictness +# second-pass verifier. +run_local_ci="${CORTEXPILOT_PRE_PUSH_RUN_CI_DOUBLE_CHECK:-0}" if [[ "$run_local_ci" == "1" ]]; then - echo "🚦 [pre-push-quality-gate] running layered local verification bundle" + echo "🚦 [pre-push-quality-gate] running strict local verification bundle (opt-in)" + bash scripts/check_secret_scan_closeout.sh --mode current + bash scripts/check_trivy_repo_scan.sh + bash scripts/run_governance_py.sh scripts/check_github_security_alerts.py --mode require --repo xiaojiou176-open/CortexPilot-public + bash scripts/run_governance_py.sh scripts/check_developer_facing_english.py + bash scripts/run_governance_py.sh scripts/check_third_party_asset_registry.py + bash scripts/run_governance_py.sh scripts/check_root_semantic_cleanliness.py + bash scripts/run_governance_py.sh scripts/check_workflow_runner_governance.py + bash scripts/run_governance_py.sh scripts/check_docs_render_freshness.py + bash scripts/run_governance_py.sh scripts/refresh_governance_evidence_manifest.py + bash scripts/run_governance_py.sh scripts/build_governance_scorecard.py --enforce + bash scripts/run_governance_py.sh scripts/build_governance_closeout_report.py --mode pre-push + bash scripts/run_governance_py.sh scripts/check_active_report_identity.py PRE_PUSH_EXTERNAL_PROBE_PROVIDER_MODE="$(resolve_pre_push_probe_provider_mode_or_fail)" # Incremental test mode: only run tests related to changed files @@ -260,8 +259,11 @@ EOF --provider-api-mode "${PRE_PUSH_EXTERNAL_PROBE_PROVIDER_MODE}" \ --hard-timeout-sec "${CORTEXPILOT_PRE_PUSH_EXTERNAL_PROBE_TIMEOUT_SEC:-120}" elif [[ "$run_local_ci" == "0" ]]; then + echo "🚦 [pre-push-quality-gate] running fast local verification bundle (default)" + bash ./scripts/test_quick.sh --no-related +elif [[ "$run_local_ci" == "off" ]]; then if [[ "${CORTEXPILOT_PRE_PUSH_BREAK_GLASS:-0}" != "1" ]]; then - echo "❌ [pre-push-quality-gate] CI double-check disabled without break-glass" >&2 + echo "❌ [pre-push-quality-gate] off mode requires break-glass" >&2 echo "Set CORTEXPILOT_PRE_PUSH_BREAK_GLASS=1 with reason/ticket to bypass." >&2 exit 1 fi @@ -275,13 +277,13 @@ elif [[ "$run_local_ci" == "0" ]]; then "${CORTEXPILOT_PRE_PUSH_BREAK_GLASS_REASON}" \ "${CORTEXPILOT_PRE_PUSH_BREAK_GLASS_TICKET}" \ "run_local_ci=${run_local_ci}")" - echo "⚠️ [pre-push-quality-gate] break-glass: skip local CI double-check" + echo "⚠️ [pre-push-quality-gate] break-glass: skip all local verification bundles" echo " reason=${CORTEXPILOT_PRE_PUSH_BREAK_GLASS_REASON}" echo " ticket=${CORTEXPILOT_PRE_PUSH_BREAK_GLASS_TICKET}" echo " audit_log=${audit_log_path}" else - echo "❌ [pre-push-quality-gate] invalid CORTEXPILOT_PRE_PUSH_RUN_CI_DOUBLE_CHECK=${run_local_ci}" >&2 + echo "❌ [pre-push-quality-gate] invalid CORTEXPILOT_PRE_PUSH_RUN_CI_DOUBLE_CHECK=${run_local_ci} (expected: 0|1|off)" >&2 exit 1 fi -echo "✅ [pre-push-quality-gate] local-first heavy gate passed" +echo "✅ [pre-push-quality-gate] local-first layered gate passed" diff --git a/scripts/render_docs.py b/scripts/render_docs.py index 433301f..226e057 100644 --- a/scripts/render_docs.py +++ b/scripts/render_docs.py @@ -103,6 +103,7 @@ def _inject_fragments() -> None: def main() -> int: args = parse_args() if not args.inject_only: + _run(["python3", "scripts/generate_storefront_proof_pack_index.py"]) _run(["python3", "scripts/ui_button_inventory.py", "--surface", "all"]) _run(["python3", "scripts/sync_ui_button_matrix.py", "--tiers", "P0,P1"]) _write_fragments() diff --git a/tooling/search/search_engine.py b/tooling/search/search_engine.py index 2d5f5ce..47d3ec6 100644 --- a/tooling/search/search_engine.py +++ b/tooling/search/search_engine.py @@ -271,20 +271,45 @@ def _write_web_error_artifacts(artifacts_dir: Path, error: str, page: Any | None def _pick_chat_input_locator(page: Any) -> Any | None: selectors = ( - "textarea", - "input[type='text']", + "[data-placeholder='Ask anything'][contenteditable='true']", + ".tiptap.ProseMirror[contenteditable='true']", + "[aria-label='Enter a prompt for Gemini'][contenteditable='true']", "[role='textbox'][contenteditable='true']", "[contenteditable='true'][role='textbox']", - "[aria-label='Enter a prompt for Gemini'][contenteditable='true']", ".ql-editor[contenteditable='true']", + "textarea", + "input[type='text']", ) for selector in selectors: locator = page.locator(selector) - if locator.count() > 0: + if locator.count() <= 0: + continue + if not hasattr(locator, "nth"): return locator.first + for index in range(locator.count()): + candidate = locator.nth(index) + try: + if candidate.is_visible(): + return candidate + except Exception: # noqa: BLE001 + return locator.first return None +def _activate_chat_input(locator: Any) -> None: + try: + locator.click(timeout=5000) + return + except Exception: + pass + try: + locator.click(timeout=5000, force=True) + return + except Exception: + pass + locator.focus() + + def _chat_provider_search( query: str, provider: str, @@ -353,7 +378,7 @@ def _chat_provider_search( locator = _pick_chat_input_locator(page) if locator is None: raise RuntimeError("input box not found") - locator.click() + _activate_chat_input(locator) locator.fill(query) locator.press("Enter") page.wait_for_timeout(3000) diff --git a/tooling/search_pipeline.py b/tooling/search_pipeline.py index 8a4a726..2e26e18 100644 --- a/tooling/search_pipeline.py +++ b/tooling/search_pipeline.py @@ -275,24 +275,24 @@ def _build_digest_result( break normalized_status = str(status_override or "").strip().upper() - template_label = "资讯摘要" if task_template == "news_digest" else "主题简报" + template_label = "news digest" if task_template == "news_digest" else "topic brief" if normalized_status == "FAILED": summary = ( - f"“{topic}”{template_label}未能完成。" - f" {failure_reason_zh or '检索链路未通过,请查看高级证据获取详细失败上下文。'}" + f"The {template_label} for '{topic}' did not complete successfully." + " Review failure_reason_zh and the evidence bundle for the detailed provider failure context." ).strip() status = "FAILED" elif digest_sources: - preview = "、".join(item["title"] for item in digest_sources[:3]) + preview = ", ".join(item["title"] for item in digest_sources[:3]) summary = ( - f"已围绕“{topic}”汇总 {len(digest_sources)} 条公开来源,覆盖最近 {time_range} 的检索结果。" - f" 当前优先可读来源包括:{preview}。" + f"Collected {len(digest_sources)} public-source result(s) about '{topic}' from the last {time_range}." + f" Current readable source highlights: {preview}." ) status = "SUCCESS" failure_reason_zh = None else: - summary = f"未检索到与“{topic}”相关的公开来源结果,请稍后重试或调整检索范围。" + summary = f"No public-source results were found for '{topic}'. Retry later or widen the search scope." status = "EMPTY" failure_reason_zh = failure_reason_zh or "未检索到公开来源结果"