Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion apps/dashboard/app/pm/components/PMIntakeLeftSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ export default function PMIntakeLeftSidebar(props: Props) {
onFocusInput,
} = props;
const activeSession = sessionHistory.find((session) => session.pm_session_id === intakeId);
const showBlockingHistoryError = Boolean(sessionHistoryError) && (Boolean(intakeId) || sessionHistory.length > 0);
const showFirstRunHistoryNotice = Boolean(sessionHistoryError) && !showBlockingHistoryError;
const activeSessionLabel = intakeId
? `${locale === "zh-CN" ? "当前会话" : "Current session"}: ${
activeSession
Expand Down Expand Up @@ -119,7 +121,14 @@ export default function PMIntakeLeftSidebar(props: Props) {
{activeSessionLabel}
</p>

{sessionHistoryError && <p className="alert alert-danger" role="alert">{sessionHistoryError}</p>}
{showBlockingHistoryError ? <p className="alert alert-danger" role="alert">{sessionHistoryError}</p> : null}
{showFirstRunHistoryNotice ? (
<p className="alert alert-warning" role="status">
{locale === "zh-CN"
? "会话历史暂时不可用。你仍然可以直接发出第一条请求,系统会创建正式会话。"
: "Session history is temporarily unavailable. You can still start the first request and create the formal session."}
</p>
) : null}
{newConversationError && (
<p className="alert alert-danger" role="alert" data-testid="pm-new-conversation-error">
{newConversationError}
Expand Down
2 changes: 1 addition & 1 deletion apps/dashboard/app/workflows/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ export default async function WorkflowsPage() {
<span className="muted">{workflowListPageCopy.emptyTitle}</span>
<span className="mono muted">{workflowListPageCopy.emptyHint}</span>
<Button asChild variant="default">
<Link href="/pm">打开 PM 入口</Link>
<Link href="/pm">{locale === "zh-CN" ? "打开 PM 入口" : "Open PM intake"}</Link>
</Button>
</div>
</Card>
Expand Down
8 changes: 8 additions & 0 deletions apps/dashboard/tests/home_page.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,14 @@ describe("dashboard home run-summary clarity", () => {
expect(String(zhMetadata.description)).toContain("证明与回放桌面");
});

it("renders the English runs desk with English-first copy", async () => {
render(await RunsPage({ searchParams: Promise.resolve({}) }));

expect(screen.getByRole("heading", { name: "Proof & Replay" })).toBeInTheDocument();
expect(screen.queryByRole("heading", { name: "证明与回放" })).not.toBeInTheDocument();
expect(screen.getByText("This desk should answer one question first: which run deserves proof right now.")).toBeInTheDocument();
});

it("shows degraded risk summary when runs data is unavailable", async () => {
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
mockFetchRuns.mockRejectedValueOnce(new Error("runs down"));
Expand Down
5 changes: 4 additions & 1 deletion apps/dashboard/tests/pm_page_chat.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -769,7 +769,10 @@ describe("pm page chat-driven flow", () => {
mockFetchPmSessions.mockRejectedValue(new Error("network down"));
render(<PMIntakePage />);

expect(await screen.findByRole("alert")).toHaveTextContent("Failed to load session history: network error, please try again.");
expect(screen.queryByRole("alert")).not.toBeInTheDocument();
expect(
await screen.findByText("Session history is temporarily unavailable. You can still start the first request and create the formal session.")
).toBeInTheDocument();
});

it("cancels in-flight chat request and keeps input draft", async () => {
Expand Down
46 changes: 46 additions & 0 deletions apps/dashboard/tests/pm_page_stage_flow.suite.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,52 @@ describe("pm intake component branches", () => {
expect(screen.getByText("Loading session history")).toBeInTheDocument();
});

it("keeps first-run session history failures as a non-blocking status note", () => {
render(
<PMIntakeLeftSidebar
{...buildLeftSidebarProps({
sessionHistoryError: "Failed to load session history: network error, please try again.",
sessionHistory: [],
intakeId: "",
})}
/>,
);

expect(screen.queryByRole("alert")).not.toBeInTheDocument();
expect(
screen.getByText("Session history is temporarily unavailable. You can still start the first request and create the formal session.")
).toBeInTheDocument();
expect(screen.getByText("No previous sessions yet. Send the first request to start.")).toBeInTheDocument();
});

it("keeps session-history failures blocking once a real session exists", () => {
render(
<PMIntakeLeftSidebar
{...buildLeftSidebarProps({
intakeId: "pm-history-1",
sessionHistoryError: "Failed to load session history: network error, please try again.",
sessionHistory: [
{
pm_session_id: "pm-history-1",
status: "active",
run_count: 1,
running_runs: 1,
failed_runs: 0,
success_runs: 0,
blocked_runs: 0,
latest_run_id: "run-1",
current_role: "PM",
current_step: "plan",
updated_at: "2026-03-01T10:00:00.000Z",
},
],
})}
/>,
);

expect(screen.getByRole("alert")).toHaveTextContent("Failed to load session history: network error, please try again.");
});

it("covers center panel keyboard link actions and inline chain expand action", () => {
const onLayoutModeChange = vi.fn();
const onHoveredChainRoleChange = vi.fn();
Expand Down
12 changes: 12 additions & 0 deletions apps/dashboard/tests/workflows_queue_page.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,16 @@ describe("workflows queue page", () => {
expect(screen.getByRole("link", { name: /打开 PM 入口|Open PM intake/ })).toHaveAttribute("href", "/pm");
expect(screen.getByText(/No workflow cases yet|当前还没有工作流案例/)).toBeInTheDocument();
});

it("keeps the English empty-state CTA in English", async () => {
vi.mocked(fetchWorkflows).mockResolvedValue([] as never);
vi.mocked(fetchQueue).mockResolvedValue([] as never);
vi.mocked(mutationExecutionCapability).mockReturnValue({ executable: false, operatorRole: null } as never);

const view = await WorkflowsPage();
render(view);

expect(screen.getByRole("link", { name: "Open PM intake" })).toHaveAttribute("href", "/pm");
expect(screen.queryByRole("link", { name: "打开 PM 入口" })).not.toBeInTheDocument();
});
});
28 changes: 28 additions & 0 deletions apps/orchestrator/tests/test_ci_current_run_report_contracts.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import json
import os
import subprocess
import sys
from datetime import datetime, timezone
Expand Down Expand Up @@ -246,6 +247,33 @@ def test_current_run_consistency_downgrades_stale_local_advisory_to_advisory(tmp
assert "source_sha_mismatch" in payload["authority_reasons"]


def test_ci_control_plane_doctor_can_emit_current_run_source_metadata(tmp_path: Path) -> None:
out_dir = tmp_path / "doctor"
env = {
"OPENVIBECODING_CI_CONTROL_PLANE_DOCTOR_OUT_DIR": str(out_dir),
"OPENVIBECODING_DOCTOR_REQUIRE_DOCKER": "0",
"OPENVIBECODING_DOCTOR_REQUIRE_SUDO": "0",
"RUNNER_TEMP": str(tmp_path / "runner-temp"),
"OPENVIBECODING_CI_SOURCE_RUN_ID": "local-run",
"OPENVIBECODING_CI_SOURCE_ROUTE": "local_full_ci",
"OPENVIBECODING_CI_SOURCE_EVENT": "local",
}
proc = subprocess.run(
["bash", str(REPO_ROOT / "scripts" / "ci_control_plane_doctor.sh")],
cwd=REPO_ROOT,
text=True,
capture_output=True,
env={**os.environ, **env},
check=False,
)

assert proc.returncode == 0, proc.stderr or proc.stdout
payload = json.loads((out_dir / "report.json").read_text(encoding="utf-8"))
assert payload["source_run_id"] == "local-run"
assert payload["source_route"] == "local_full_ci"
assert payload["source_event"] == "local"


def test_artifact_index_marks_head_mismatch_non_authoritative(tmp_path: Path) -> None:
route_report = tmp_path / "trusted_pr.json"
route_report.write_text("{}", encoding="utf-8")
Expand Down
39 changes: 39 additions & 0 deletions configs/env.registry.json
Original file line number Diff line number Diff line change
Expand Up @@ -1692,6 +1692,45 @@
"scripts/lib/ci_main_impl.sh"
]
},
{
"name": "OPENVIBECODING_CI_SOURCE_EVENT",
"scope": "ci",
"secret": false,
"required": false,
"default": null,
"owner": "platform",
"description": "Current CI source event propagated into control-plane doctor and current-run provenance receipts so authoritative local/full-ci evidence can carry the triggering event class.",
"consumers": [
"scripts/ci_control_plane_doctor.sh",
"scripts/lib/ci_step126_helpers.sh"
]
},
{
"name": "OPENVIBECODING_CI_SOURCE_ROUTE",
"scope": "ci",
"secret": false,
"required": false,
"default": null,
"owner": "platform",
"description": "Current CI source route identifier propagated into control-plane doctor and current-run provenance receipts so authoritative evidence keeps the originating route contract.",
"consumers": [
"scripts/ci_control_plane_doctor.sh",
"scripts/lib/ci_step126_helpers.sh"
]
},
{
"name": "OPENVIBECODING_CI_SOURCE_RUN_ID",
"scope": "ci",
"secret": false,
"required": false,
"default": null,
"owner": "platform",
"description": "Current CI source run identifier propagated into control-plane doctor and current-run provenance receipts so authoritative evidence keeps the originating run lineage.",
"consumers": [
"scripts/ci_control_plane_doctor.sh",
"scripts/lib/ci_step126_helpers.sh"
]
},
{
"name": "OPENVIBECODING_CI_RUNNER_CLASS",
"scope": "ci",
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend-shared/uiCopy.js
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,7 @@ const UI_COPY = {
queueSummary: (count, slaState) => `queue: ${count} / SLA ${slaState}`,
},
runsPage: {
title: "证明与回放",
title: "Proof & Replay",
subtitle: "Use this spine to inspect run evidence, compare posture, and replay decisions. Failed-run triage is only one lane inside the broader proof desk.",
countsBadge: (runCount) => `${runCount} runs`,
warningTitle: "Proof & Replay is currently running with partial truth.",
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend-shared/uiCopy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1676,7 +1676,7 @@ const UI_COPY: Record<UiLocale, UiCopy> = {
queueSummary: (count: number, slaState: string) => `queue: ${count} / SLA ${slaState}`,
},
runsPage: {
title: "证明与回放",
title: "Proof & Replay",
subtitle:
"Use this spine to inspect run evidence, compare posture, and replay decisions. Failed-run triage is only one lane inside the broader proof desk.",
countsBadge: (runCount: number) => `${runCount} runs`,
Expand Down
14 changes: 13 additions & 1 deletion scripts/ci_control_plane_doctor.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,11 @@ if check_cmd curl; then curl_ok=1; fi
if [[ -n "${RUNNER_TEMP:-}" ]]; then runner_temp_ok=1; fi

allowlist_json='["OPENVIBECODING_DOC_GATE_MODE","OPENVIBECODING_DOC_GATE_BASE_SHA","OPENVIBECODING_DOC_GATE_HEAD_SHA","OPENVIBECODING_EXTERNAL_WEB_PROBE_PROVIDER_API_MODE","OPENVIBECODING_CI_LIVE_PREFLIGHT_PROVIDER_API_MODE","OPENVIBECODING_CI_EXTERNAL_WEB_PROBE_PROVIDER_API_MODE"]'
SOURCE_RUN_ID="${OPENVIBECODING_CI_SOURCE_RUN_ID:-}"
SOURCE_ROUTE="${OPENVIBECODING_CI_SOURCE_ROUTE:-}"
SOURCE_EVENT="${OPENVIBECODING_CI_SOURCE_EVENT:-}"

python3 - "$REPORT_JSON" "$REPORT_MD" "$docker_ok" "$sudo_ok" "$jq_ok" "$curl_ok" "$runner_temp_ok" "$tool_cache_ok" "$allowlist_json" <<'PY'
python3 - "$REPORT_JSON" "$REPORT_MD" "$docker_ok" "$sudo_ok" "$jq_ok" "$curl_ok" "$runner_temp_ok" "$tool_cache_ok" "$allowlist_json" "$SOURCE_RUN_ID" "$SOURCE_ROUTE" "$SOURCE_EVENT" <<'PY'
import json
import sys
from datetime import datetime, timezone
Expand All @@ -63,6 +66,9 @@ from pathlib import Path
runner_temp_ok,
tool_cache_ok,
allowlist_json,
source_run_id,
source_route,
source_event,
) = sys.argv[1:]

payload = {
Expand All @@ -77,6 +83,9 @@ payload = {
"tool_cache_contract": tool_cache_ok == "1",
},
"strict_ci_openvibecoding_allowlist": json.loads(allowlist_json),
"source_run_id": source_run_id,
"source_route": source_route,
"source_event": source_event,
}
Path(report_json).write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
Path(report_md).write_text(
Expand All @@ -90,6 +99,9 @@ Path(report_md).write_text(
f"- curl: **{payload['checks']['curl']}**",
f"- runner_temp: **{payload['checks']['runner_temp']}**",
f"- tool_cache_contract: **{payload['checks']['tool_cache_contract']}**",
f"- source_run_id: `{payload['source_run_id']}`",
f"- source_route: `{payload['source_route']}`",
f"- source_event: `{payload['source_event']}`",
f"- strict_ci_openvibecoding_allowlist: `{', '.join(payload['strict_ci_openvibecoding_allowlist'])}`",
"",
]
Expand Down
7 changes: 7 additions & 0 deletions scripts/lib/ci_step126_helpers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ run_ci_step126_current_run_fanin() {
--github-ref "${GITHUB_REF:-local}" \
--github-event-name "${GITHUB_EVENT_NAME:-local}" \
--job-observed "release-evidence"
local_runner_temp="${RUNNER_TEMP:-${CI_REPORT_ROOT}/runner_temp}"
mkdir -p "${local_runner_temp}"
OPENVIBECODING_CI_SOURCE_RUN_ID="${GITHUB_RUN_ID:-local-run}" \
OPENVIBECODING_CI_SOURCE_ROUTE="${CI_ROUTE_ID}" \
OPENVIBECODING_CI_SOURCE_EVENT="${GITHUB_EVENT_NAME:-local}" \
RUNNER_TEMP="${local_runner_temp}" \
bash scripts/ci_control_plane_doctor.sh
declare -a CI_SOURCE_MANIFEST_ARGS=(
--output "${CI_SOURCE_MANIFEST_PATH}"
--route-id "${CI_ROUTE_ID}"
Expand Down
28 changes: 27 additions & 1 deletion scripts/truth_triage.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ cd "$ROOT_DIR"
CURRENT_RUN_ROOT=".runtime-cache/openvibecoding/reports/ci/current_run"
CURRENT_RUN_ROUTE_REPORT=".runtime-cache/openvibecoding/reports/ci/routes/local-advisory.json"
CURRENT_RUN_SOURCE_MANIFEST="${CURRENT_RUN_ROOT}/source_manifest.json"
CURRENT_RUN_CONSISTENCY_JSON="${CURRENT_RUN_ROOT}/consistency.json"

cleanup_forbidden_python_residue() {
local found=0
Expand Down Expand Up @@ -56,6 +57,27 @@ build_local_advisory_current_run_manifest() {
--route-report "$CURRENT_RUN_ROUTE_REPORT"
}

preserve_authoritative_current_run_manifest() {
python3 - <<'PY'
import json
from pathlib import Path

manifest = Path(".runtime-cache/openvibecoding/reports/ci/current_run/source_manifest.json")
consistency = Path(".runtime-cache/openvibecoding/reports/ci/current_run/consistency.json")
if not manifest.is_file() or not consistency.is_file():
raise SystemExit(1)

manifest_payload = json.loads(manifest.read_text(encoding="utf-8"))
consistency_payload = json.loads(consistency.read_text(encoding="utf-8"))
is_authoritative = bool(consistency_payload.get("authoritative_current_truth"))
source_route = str(manifest_payload.get("source_route") or "").strip()

if is_authoritative and source_route and source_route != "local-advisory":
raise SystemExit(0)
raise SystemExit(1)
PY
}

echo "🧭 [truth-triage] start"
echo
echo "== normalize transient residue =="
Expand All @@ -71,7 +93,11 @@ bash scripts/run_governance_py.sh scripts/check_upstream_same_run_cohesion.py
echo

echo "== current-run truth =="
build_local_advisory_current_run_manifest
if preserve_authoritative_current_run_manifest; then
echo "ℹ️ [truth-triage] preserving existing authoritative current-run manifest"
else
build_local_advisory_current_run_manifest
fi
bash scripts/run_governance_py.sh scripts/check_ci_current_run_sources.py --source-manifest "$CURRENT_RUN_SOURCE_MANIFEST"
echo

Expand Down
Loading