feat(web): add Code Review Codex handoff board
All checks were successful
CD Pipeline / tests (push) Successful in 1m25s
Code Review / ai-code-review (push) Successful in 13s
CD Pipeline / build-and-deploy (push) Successful in 3m33s
CD Pipeline / post-deploy-checks (push) Successful in 1m55s

This commit is contained in:
Your Name
2026-06-04 11:50:19 +08:00
parent 9bac66382b
commit 711e102d87
2 changed files with 184 additions and 0 deletions

View File

@@ -4,11 +4,16 @@ import { AppLayout } from '@/components/layout'
import { IwoooSReadOnlyBridge } from '@/components/security/iwooos-read-only-bridge'
import {
Activity,
ArrowRight,
Bot,
CheckCircle2,
ClipboardList,
Code2,
ExternalLink,
FileCheck2,
GitBranch,
Gauge,
LockKeyhole,
SearchCheck,
ShieldCheck,
} from 'lucide-react'
@@ -33,6 +38,71 @@ const agentStateLabel: Record<string, string> = {
standby: '待命',
}
const codexHandoffLanes = [
{
icon: Code2,
title: '可交給 Codex 起草',
metric: '4 類',
detail: '前端體驗、測試補洞、文件同步、低風險重構。',
guard: '只建立草稿修正與驗證清單。',
className: 'border-emerald-500/30 bg-emerald-500/10 text-emerald-100',
iconClassName: 'text-emerald-300',
},
{
icon: FileCheck2,
title: '需人工批准後接手',
metric: '3 類',
detail: '高風險程式路徑、部署流程、跨專案設定。',
guard: '先有人控範圍、回滾條件與證據。',
className: 'border-amber-500/30 bg-amber-500/10 text-amber-100',
iconClassName: 'text-amber-300',
},
{
icon: LockKeyhole,
title: '禁止自動轉工作',
metric: '5 條',
detail: 'Kali 主機變更、掃描、正式推版、主要來源切換、執行期閘門。',
guard: '維持 Gate 0 與獨立人工批准。',
className: 'border-rose-500/30 bg-rose-500/10 text-rose-100',
iconClassName: 'text-rose-300',
},
]
const codexCandidateWork = [
{
id: 'CR-CX-01',
track: '前端體驗',
finding: '版面密度、手機寬度、文字層級',
handoff: 'Codex 起草 UI 修正與截圖證據',
gate: '人工確認 scope',
state: '候選',
},
{
id: 'CR-CX-02',
track: '守門測試',
finding: 'IwoooS 邊界、禁用動作、進度口徑',
handoff: '補 guard / smoke test',
gate: '不得碰 runtime',
state: '候選',
},
{
id: 'CR-CX-03',
track: '繁中紀錄',
finding: '部署證據、LOGBOOK、跨 Session 同步',
handoff: '整理已驗證事實',
gate: '不寫未驗證結論',
state: '候選',
},
{
id: 'CR-CX-04',
track: '禁止轉派',
finding: 'Kali 更新、掃描、GitHub primary、正式部署',
handoff: '維持人工閘門',
gate: '獨立批准',
state: '封鎖',
},
]
export default function CodeReviewPage({ params }: { params: { locale: string } }) {
const evidenceHref = `/${params.locale}/awooop/runs`
@@ -62,6 +132,102 @@ export default function CodeReviewPage({ params }: { params: { locale: string }
<IwoooSReadOnlyBridge variant="dark" />
<section
id="code-review-codex-handoff-board"
data-testid="code-review-codex-handoff-board"
className="grid min-w-0 gap-4 rounded border border-gray-800 bg-gray-950 p-4 lg:grid-cols-[minmax(0,0.92fr)_minmax(0,1.08fr)]"
>
<div className="flex min-w-0 flex-col justify-between gap-5">
<div className="min-w-0">
<div className="flex items-center gap-2 text-xs font-mono uppercase text-sky-300">
<ClipboardList className="h-4 w-4" />
Code Review Codex
</div>
<h2 className="mt-2 text-xl font-semibold text-white"> Coding </h2>
<p className="mt-2 max-w-xl text-sm leading-6 text-gray-400">
Codex 稿
</p>
</div>
<div className="grid gap-2 text-xs font-mono text-gray-400 sm:grid-cols-2">
<div className="min-w-0 break-all rounded border border-gray-800 bg-black/25 px-3 py-2">
codex_handoff_mode=read_only_candidate
</div>
<div className="min-w-0 break-all rounded border border-gray-800 bg-black/25 px-3 py-2">
codex_auto_coding_enabled=false
</div>
<div className="min-w-0 break-all rounded border border-gray-800 bg-black/25 px-3 py-2">
runtime_execution_authorized=false
</div>
<div className="min-w-0 break-all rounded border border-gray-800 bg-black/25 px-3 py-2">
production_deploy_authorized=false
</div>
</div>
</div>
<div className="grid min-w-0 gap-3 md:grid-cols-3">
{codexHandoffLanes.map((lane, index) => {
const Icon = lane.icon
return (
<div
key={lane.title}
className={`flex min-h-[12rem] min-w-0 flex-col rounded border p-3 ${lane.className}`}
>
<div className="flex items-center justify-between gap-3">
<Icon className={`h-5 w-5 ${lane.iconClassName}`} />
<span className="font-mono text-xl font-semibold">{lane.metric}</span>
</div>
<div className="mt-4 text-sm font-semibold text-white">{lane.title}</div>
<p className="mt-2 text-sm leading-5 text-gray-300">{lane.detail}</p>
<div className="mt-auto pt-4 text-xs leading-5 text-gray-400">{lane.guard}</div>
{index < codexHandoffLanes.length - 1 ? (
<ArrowRight className="mt-3 hidden h-4 w-4 self-end text-gray-600 md:block" />
) : null}
</div>
)
})}
</div>
</section>
<section
data-testid="code-review-codex-candidate-work"
className="min-w-0 rounded border border-gray-800 bg-gray-950"
>
<div className="grid min-w-0 gap-2 border-b border-gray-800 px-4 py-3 md:grid-cols-[minmax(0,1fr)_auto] md:items-center">
<div className="min-w-0">
<div className="break-words text-sm font-semibold text-white">Codex </div>
<div className="mt-1 break-words text-xs text-gray-500">
稿
</div>
</div>
<div className="min-w-0 max-w-full break-all rounded border border-emerald-500/30 px-3 py-1 text-xs font-mono text-emerald-200 md:justify-self-end">
action_buttons_allowed=false
</div>
</div>
<div className="divide-y divide-gray-800">
{codexCandidateWork.map((item) => (
<div
key={item.id}
className="grid gap-3 px-4 py-3 text-sm md:grid-cols-[7rem_8rem_1.2fr_1.1fr_8rem_5rem] md:items-center"
>
<span className="min-w-0 break-words font-mono text-gray-500">{item.id}</span>
<span className="min-w-0 break-words font-semibold text-white">{item.track}</span>
<span className="min-w-0 break-words text-gray-400">{item.finding}</span>
<span className="min-w-0 break-words text-gray-300">{item.handoff}</span>
<span className="min-w-0 break-words text-gray-500">{item.gate}</span>
<span
className={
item.state === '封鎖'
? 'justify-self-start rounded bg-rose-500/10 px-2 py-1 text-xs font-mono text-rose-200'
: 'justify-self-start rounded bg-emerald-500/10 px-2 py-1 text-xs font-mono text-emerald-200'
}
>
{item.state}
</span>
</div>
))}
</div>
</section>
<section className="grid gap-3 md:grid-cols-4">
<div className="rounded border border-gray-800 bg-gray-950 p-4">
<div className="flex items-center gap-2 text-xs text-gray-400">

View File

@@ -8297,6 +8297,24 @@ def validate(root: Path) -> None:
source_text,
'<IwoooSReadOnlyBridge variant="dark" />',
)
for text in [
'data-testid="code-review-codex-handoff-board"',
'data-testid="code-review-codex-candidate-work"',
"審查後 Coding 工作橋接",
"Codex 工作草稿",
"可交給 Codex 起草",
"需人工批准後接手",
"禁止自動轉工作",
"codex_handoff_mode=read_only_candidate",
"codex_auto_coding_enabled=false",
"runtime_execution_authorized=false",
"production_deploy_authorized=false",
"action_buttons_allowed=false",
"CR-CX-04",
"Kali 更新、掃描、GitHub primary、正式部署",
"維持人工閘門",
]:
assert_text_contains("code_review_page.codex_handoff_read_only", code_review_page, text)
assert_text_contains("iwooos_page.surface_connection_board", iwooos_projection_page, "surfaceConnectionStatuses")
assert_text_contains("iwooos_page.surface_connection_testid", iwooos_projection_page, 'data-testid="iwooos-surface-connection-board"')
assert_text_contains("iwooos_page.surface_connection_embedded", iwooos_projection_page, "embeddedBridge")