feat(web): show GitHub readiness in IwoooS

This commit is contained in:
Your Name
2026-05-20 20:54:44 +08:00
parent a26ed24a62
commit a8a8e9d022
11 changed files with 506 additions and 3 deletions

View File

@@ -2837,6 +2837,44 @@
}
}
},
"sourceControlReadiness": {
"title": "GitHub Primary Readiness",
"subtitle": "The long-term Gitea-to-GitHub direction is shown as read-only readiness: candidate repos, owner responses, refs truth, workflow / secret names, and rollback ADR must all be present before primary_ready_count can be reviewed.",
"gateLabel": "Readiness gate",
"guardLabel": "Still forbidden",
"items": {
"candidateRepos": {
"title": "Candidate repo inventory",
"body": "8 candidate repos and 7 in-scope repos are visible for inventory and owner-evidence alignment only.",
"guard": "No GitHub repo creation or visibility changes."
},
"primaryReady": {
"title": "primary_ready_count remains 0",
"body": "No repo has passed target, refs, workflow / secret name, and rollback readiness yet.",
"guard": "No GitHub primary switch and no Gitea disablement."
},
"ownerResponses": {
"title": "Owner responses are still waiting",
"body": "S4.9-S4.12 include 22 templates; received=0 and accepted=0.",
"guard": "Do not treat request-ready as response accepted."
},
"refsTruth": {
"title": "Refs truth is not accepted",
"body": "main / dev truth, release tags, and deprecated refs still need owner decisions.",
"guard": "No refs push, delete, or force push."
},
"workflowSecrets": {
"title": "Workflow / secret names are incomplete",
"body": "Workflow, runner, webhook, and secret-name parity evidence is still missing for 7 in-scope repos.",
"guard": "Collect names and owners only, never secret values."
},
"rollbackAdr": {
"title": "Rollback ADR is not approved",
"body": "Rollback owner, validation window, and trigger details still need human review for 7 in-scope repos.",
"guard": "No cutover dry-run and no primary switch."
}
}
},
"awooopReadOnlyLandingReadiness": {
"title": "AwoooP Read-Only Landing Readiness",
"subtitle": "S2.51 turns the AwoooP main-line read-only consumption path for IwoooS / security mirror state into an intake readiness board. This is landing readiness, not production_landing_enabled, and it does not connect an execution router.",

View File

@@ -2838,6 +2838,44 @@
}
}
},
"sourceControlReadiness": {
"title": "GitHub Primary Readiness",
"subtitle": "把 Gitea 轉 GitHub 的長期方向拆成只讀 readiness候選 repo、owner response、refs truth、workflow / secret 名稱與 rollback ADR 都要到齊,才會重估 primary_ready_count。",
"gateLabel": "Readiness gate",
"guardLabel": "仍禁止",
"items": {
"candidateRepos": {
"title": "候選 repo 清冊",
"body": "8 個 candidate repos、7 個 in-scope目前只做清冊與 owner evidence 對齊。",
"guard": "不建立 GitHub repo、不改 visibility。"
},
"primaryReady": {
"title": "primary_ready_count 仍為 0",
"body": "尚無 repo 通過 target、refs、workflow / secret 名稱與 rollback readiness。",
"guard": "不切 GitHub primary、不停用 Gitea。"
},
"ownerResponses": {
"title": "Owner response 仍等待",
"body": "S4.9-S4.12 共 22 個 templates目前 received=0、accepted=0。",
"guard": "不把 request-ready 當 response accepted。"
},
"refsTruth": {
"title": "Refs truth 尚未接受",
"body": "main / dev truth、release tags、deprecated refs 仍需 owner 判定。",
"guard": "不 push、delete、force push refs。"
},
"workflowSecrets": {
"title": "Workflow / secret 名稱未完成",
"body": "7 個 in-scope repos 的 workflow、runner、webhook、secret name parity 還缺 evidence。",
"guard": "只收名稱與 owner不收 secret value。"
},
"rollbackAdr": {
"title": "Rollback ADR 未批准",
"body": "7 個 in-scope repos 的 rollback owner、驗證窗口與 trigger 尚待人工審查。",
"guard": "不 dry-run cutover、不切 primary。"
}
}
},
"awooopReadOnlyLandingReadiness": {
"title": "AwoooP 只讀 Landing Readiness",
"subtitle": "S2.51 把 AwoooP 主線要如何只讀消費 IwoooS / security mirror 狀態整理成接入準備面板。這是 landing readiness不是 production landing enabled也不接 execution router。",

View File

@@ -66,6 +66,14 @@ type OwnerResponseNextActionFocusItem = {
tone: 'steady' | 'warn' | 'locked'
}
type SourceControlReadinessItem = {
key: string
gate: string
value: string
icon: typeof ShieldCheck
tone: 'steady' | 'warn' | 'locked'
}
type S49OwnerResponsePreflightCheck = {
key: string
check: string
@@ -406,6 +414,15 @@ const ownerResponseNextActionFocusItems: OwnerResponseNextActionFocusItem[] = [
{ key: 'workflowSecretOwnerResponse', focus: 'S4.12', icon: Lock, tone: 'locked' },
]
const sourceControlReadinessItems: SourceControlReadinessItem[] = [
{ key: 'candidateRepos', gate: 'S4.0', value: '8 / 7', icon: GitBranch, tone: 'warn' },
{ key: 'primaryReady', gate: 'S4.0', value: '0', icon: Lock, tone: 'locked' },
{ key: 'ownerResponses', gate: 'S4.13', value: '0 / 22', icon: ClipboardCheck, tone: 'warn' },
{ key: 'refsTruth', gate: 'S4.11', value: '0', icon: SearchCheck, tone: 'locked' },
{ key: 'workflowSecrets', gate: 'S4.12', value: '0 / 7', icon: FileWarning, tone: 'warn' },
{ key: 'rollbackAdr', gate: 'S4.4', value: '0', icon: FileText, tone: 'locked' },
]
const s49OwnerResponsePreflightChecks: S49OwnerResponsePreflightCheck[] = [
{ key: 'knownAttestationItem', check: 'P1', icon: SearchCheck, tone: 'warn' },
{ key: 'requiredOwnerFields', check: 'P2', icon: ListChecks, tone: 'warn' },
@@ -1075,6 +1092,38 @@ function OwnerResponseNextActionFocusCard({ item }: { item: OwnerResponseNextAct
)
}
function SourceControlReadinessCard({ item }: { item: SourceControlReadinessItem }) {
const t = useTranslations('iwooos.sourceControlReadiness')
const Icon = item.icon
const textWrap = { overflowWrap: 'anywhere' as const, wordBreak: 'break-word' as const }
return (
<div style={{ ...band, minHeight: 184, padding: 16, ...textWrap }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 9 }}>
<Icon size={18} color={toneColors[item.tone]} />
<span style={{ fontSize: 11, color: '#87867f' }}>{t('gateLabel')}</span>
</div>
<span style={{ fontSize: 11, color: toneColors[item.tone] }}>{item.gate}</span>
</div>
<div style={{ fontSize: 28, fontWeight: 700, color: '#141413', marginTop: 12, lineHeight: 1 }}>
{item.value}
</div>
<h2 style={{ fontSize: 14, margin: '10px 0 6px', color: '#141413' }}>
{t(`items.${item.key}.title` as never)}
</h2>
<p style={{ fontSize: 12, lineHeight: 1.55, color: '#6f6d66', margin: 0, ...textWrap }}>
{t(`items.${item.key}.body` as never)}
</p>
<div style={{ marginTop: 10, display: 'grid', gap: 5 }}>
<div style={{ fontSize: 11, color: '#87867f' }}>{t('guardLabel')}</div>
<div style={{ fontSize: 11, color: toneColors[item.tone], lineHeight: 1.45, ...textWrap }}>
{t(`items.${item.key}.guard` as never)}
</div>
</div>
</div>
)
}
function S49OwnerResponsePreflightCard({ item }: { item: S49OwnerResponsePreflightCheck }) {
const t = useTranslations('iwooos.s49OwnerResponsePreflight')
const Icon = item.icon
@@ -2400,6 +2449,26 @@ export default function IwoooSPage({ params }: { params: { locale: string } }) {
</div>
</section>
<section style={{ marginBottom: 14 }} data-testid="iwooos-source-control-readiness-board">
<div style={{ marginBottom: 14 }}>
<h2 style={{ fontSize: 16, margin: 0 }}>{t('sourceControlReadiness.title')}</h2>
<p style={{ fontSize: 12, color: '#6f6d66', margin: '6px 0 0', lineHeight: 1.55 }}>
{t('sourceControlReadiness.subtitle')}
</p>
</div>
<div
style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))',
gap: 12,
}}
>
{sourceControlReadinessItems.map(item => (
<SourceControlReadinessCard key={item.key} item={item} />
))}
</div>
</section>
<section style={{ marginBottom: 14 }}>
<div style={{ marginBottom: 14 }}>
<h2 style={{ fontSize: 16, margin: 0 }}>{t('awooopReadOnlyLandingReadiness.title')}</h2>