diff --git a/mock/README.md b/mock/README.md index 8350ac3..8dca0e1 100644 --- a/mock/README.md +++ b/mock/README.md @@ -19,8 +19,11 @@ What to review: then choose a role. - The preview is intentionally the **user view only**: no admin page, no superadmin team console and no workspace proxy controls. -- Job lifecycle: run iterate/plan/review, open the job detail, and execute a - completed plan. +- Work items: create, triage, assign, start, and move through the state machine. +- Job lifecycle: run iterate/plan/review, open the job detail, pause/cancel/resume, + approve a plan, and execute an approved plan. +- Repository settings: plan-first, allowed/forbidden paths, merge method and + branch-protection requirement. - Global settings and the mock question inbox. - The top-right `中 / EN` switch changes the main labels; mock content stays as written. diff --git a/mock/fledge-ui.html b/mock/fledge-ui.html index 3524151..5a31405 100644 --- a/mock/fledge-ui.html +++ b/mock/fledge-ui.html @@ -229,8 +229,19 @@ ], jobs: [ { id: 'job_o6mO', kind: 'iterate', status: 'succeeded', trigger: 'schedule', summary: '修 #499 上评审 F1:balancedParenTail() 回接括号组后直接 return,带括号段的地址被截断;补 6 行组间扫描 + 回归测试。', pr: '#499', issue: '#498', cost: '$0.42', created: '2026-11-03 10:22', evidence: { tests: '36/36 pass', checks: 'CI 两绿', files: 6, risk: 'low', rollback: 'revert commit' }, attribution: 'Lead · Dev-1 · Test · Reviewer' }, - { id: 'job_7c2a', kind: 'plan', status: 'succeeded', trigger: 'manual', summary: '计划:把地址解析的括号处理抽成独立函数,补 3 个回归用例,不改变现有输出。', pr: '—', issue: '—', cost: '$0.03', created: '2026-11-02 18:10', evidence: { tests: 'not run', checks: 'not run', files: 0, risk: 'low', rollback: 'no change' }, attribution: 'Product · Test' } - ] + { id: 'job_7c2a', kind: 'plan', status: 'succeeded', trigger: 'manual', summary: '计划:把地址解析的括号处理抽成独立函数,补 3 个回归用例,不改变现有输出。', pr: '—', issue: '—', cost: '$0.03', created: '2026-11-02 18:10', evidence: { tests: 'not run', checks: 'not run', files: 0, risk: 'low', rollback: 'no change' }, attribution: 'Product · Test', planApproved: false } + ], + workItems: [ + { id: 'work_dm1', title: '修复地址括号回接', state: 'pr_ready', priority: 30, pr: '#499', issue: '#498', job: 'job_o6mO' }, + { id: 'work_dm2', title: '对比竞品记忆能力并提需求', state: 'proposed', priority: 20, pr: '—', issue: '—', job: '—' } + ], + planRequired: false, + allowPaths: 'src/', + denyPaths: '.github/', + mergeMethod: 'squash', + requireProtection: true, + protectionState: 'protected', + protectedAt: '2026-11-04 09:10' }, fledge: { id: 'fledge', @@ -253,8 +264,20 @@ questions: [], jobs: [ { id: 'job_rel10', kind: 'review', status: 'succeeded', trigger: 'schedule', summary: 'Reviewed 6 PRs; average 82. Two Fledge PRs need changes; follow-ups queued on the same branches.', pr: '—', issue: '—', cost: '$0.18', created: '2026-11-03 09:40', evidence: { tests: 'not run', checks: 'CI green', files: 0, risk: 'low', rollback: 'no change' }, attribution: 'Reviewer' }, - { id: 'job_v022', kind: 'iterate', status: 'succeeded', trigger: 'manual', summary: 'release: v0.2.2 — assembled 16 changelog fragments, bumped package/healthz version and updated release references.', pr: '#11', issue: '—', cost: '$0.09', created: '2026-11-03 10:18', evidence: { tests: '86/86 pass', checks: 'CI green', files: 27, risk: 'low', rollback: 'revert release commit' }, attribution: 'Release' } - ] + { id: 'job_v022', kind: 'iterate', status: 'succeeded', trigger: 'manual', summary: 'release: v0.2.2 — assembled 16 changelog fragments, bumped package/healthz version and updated release references.', pr: '#11', issue: '—', cost: '$0.09', created: '2026-11-03 10:18', evidence: { tests: '86/86 pass', checks: 'CI green', files: 27, risk: 'low', rollback: 'revert release commit' }, attribution: 'Release' }, + { id: 'job_plan_p0', kind: 'plan', status: 'succeeded', trigger: 'manual', summary: 'Plan: add work-item state transitions, a plan approval gate and branch-protection verification. Touch src/ and docs/ only.', pr: '—', issue: '—', cost: '$0.02', created: '2026-11-04 09:30', evidence: { tests: 'not run', checks: 'not run', files: 0, risk: 'low', rollback: 'no change' }, attribution: 'Product · Test', planApproved: false } + ], + workItems: [ + { id: 'work_fl1', title: 'P0: work items 与状态机', state: 'merged', priority: 50, pr: '#12', issue: '—', job: 'job_v022' }, + { id: 'work_fl2', title: '补充 branch protection 校验', state: 'in_review', priority: 40, pr: '#13', issue: '—', job: 'job_rel10' } + ], + planRequired: true, + allowPaths: 'src/, docs/', + denyPaths: '.github/', + mergeMethod: 'squash', + requireProtection: true, + protectionState: 'protected', + protectedAt: '2026-11-04 09:20' }, widgets: { id: 'widgets', @@ -272,7 +295,15 @@ instance: 'starting', chief: [], questions: [], - jobs: [] + jobs: [], + workItems: [], + planRequired: false, + allowPaths: '', + denyPaths: '', + mergeMethod: 'squash', + requireProtection: true, + protectionState: 'unknown', + protectedAt: null } }, }; @@ -284,10 +315,13 @@ const allowedRoles = (permission) => permission === 'admin' || permission === 'maintain' ? ['maintainer', 'developer'] : ['developer']; const roleLabel = (role) => role === 'maintainer' ? t('维护者', 'Maintainer') : role === 'developer' ? t('开发者', 'Developer') : t('未选择', 'Not selected'); const roleClass = (role) => role === 'maintainer' ? 'role-m' : role === 'developer' ? 'role-d' : ''; +const workStateLabel = (state) => ({ proposed: t('待分诊', 'Proposed'), triaged: t('已分诊', 'Triaged'), assigned: t('已分配', 'Assigned'), in_progress: t('进行中', 'In progress'), pr_ready: t('PR 已就绪', 'PR ready'), in_review: t('审核中', 'In review'), changes_requested: t('需要修改', 'Changes requested'), approved: t('已批准', 'Approved'), merged: t('已合并', 'Merged'), closed: t('已关闭', 'Closed') }[state] || state); +const workStateClass = (state) => state === 'merged' || state === 'approved' ? 'good' : state === 'closed' ? 'bad' : state === 'in_progress' || state === 'in_review' || state === 'pr_ready' || state === 'changes_requested' ? 'busy' : 'kind'; +const workNextStates = (state) => ({ proposed: ['triaged', 'closed'], triaged: ['assigned', 'closed'], assigned: ['in_progress', 'triaged', 'closed'], in_progress: ['pr_ready', 'changes_requested', 'closed'], pr_ready: ['in_review', 'changes_requested', 'merged', 'closed'], in_review: ['changes_requested', 'approved', 'merged', 'closed'], changes_requested: ['in_progress', 'closed'], approved: ['merged', 'closed'], merged: [], closed: ['triaged'] }[state] || []); const permLabel = (permission) => ({ admin: 'admin', maintain: 'maintain', write: 'write', triage: 'triage', read: 'read' }[permission] || permission); const statusBadge = (status) => { - const cls = status === 'succeeded' || status === 'approved' ? 'good' : status === 'failed' || status === 'rejected' ? 'bad' : status === 'running' || status === 'queued' || status === 'pending' ? 'busy' : ''; - const label = { succeeded: t('成功', 'Succeeded'), failed: t('失败', 'Failed'), running: t('运行中', 'Running'), queued: t('排队中', 'Queued'), approved: t('已批准', 'Approved'), pending: t('待审批', 'Pending'), idle: t('空闲', 'Idle') }[status] || status; + const cls = status === 'succeeded' || status === 'approved' ? 'good' : status === 'failed' || status === 'rejected' || status === 'cancelled' ? 'bad' : status === 'running' || status === 'queued' || status === 'pending' || status === 'paused' ? 'busy' : ''; + const label = { succeeded: t('成功', 'Succeeded'), failed: t('失败', 'Failed'), running: t('运行中', 'Running'), queued: t('排队中', 'Queued'), paused: t('已暂停', 'Paused'), cancelled: t('已取消', 'Cancelled'), approved: t('已批准', 'Approved'), pending: t('待审批', 'Pending'), idle: t('空闲', 'Idle') }[status] || status; return `${esc(label)}`; }; const kindLabel = (kind) => kind === 'review' ? t('审核', 'Review') : kind === 'plan' ? t('计划', 'Plan') : t('迭代', 'Iterate'); @@ -491,6 +525,25 @@
${repo.instance === 'ready' ? statusBadge('ready') : statusBadge('pending')} ${esc(t('实例', 'Instance'))} repo-${esc(repo.id)}
${esc(t('任务先发给 Chief,再由负责人执行。', 'Jobs go to the Chief, then the Lead.'))}
`; + const workItems = repo.workItems || []; + const workRows = workItems.length === 0 + ? `| ${esc(t('工作项', 'Work item'))} | ${esc(t('状态', 'State'))} | ${esc(t('优先级', 'Priority'))} | Issue / PR |
|---|
${esc(t('任务不存在。', 'Job not found.'))}
${job.planApproved ? esc(t('计划已批准,可以执行。', 'Plan approved; ready to execute.')) : job.planRejected ? esc(t('计划已被拒绝。', 'Plan rejected.')) : esc(t('等待 maintainer 批准计划。', 'Waiting for maintainer approval.'))}
${esc(job.id)}${esc(repo.owner)}/${esc(repo.name)} · ${statusBadge(job.status)} · ${esc(kindLabel(job.kind))} · ${esc(triggerLabel(job.trigger))} · ${esc(t('创建', 'Created'))} ${esc(job.created)}
${esc(job.id)}${esc(repo.owner)}/${esc(repo.name)} · ${statusBadge(job.status)} · ${esc(kindLabel(job.kind))} · ${esc(triggerLabel(job.trigger))} · ${esc(t('创建', 'Created'))} ${esc(job.created)}
${esc(job.summary)}
${esc(t('成本', 'Cost'))} ${esc(job.cost)} · ${esc(t('归因', 'Attribution'))} ${esc(job.attribution)}
PR ${esc(job.pr)} · Issue ${esc(job.issue)}